集卡片

来源:互联网 发布:微课录免费中文软件 编辑:程序博客网 时间:2024/05/15 14:18

题目描述

boboo小时候很喜欢收集卡片,他经常要去商店购买新到的卡片。
商店出售的卡片有N张,是连续的,并且都连在一起成为一个长串,商店阿姨告诉boboo只能购买连续的一段,这一串卡片共有M种,每种卡片都有一个价格,boboo拿的钱数为V,他想花最少的钱来集齐所有种类的卡片,你能帮帮他吗?
输入文件
第1行 三个正整数 N,M,V
第2行共M个正整数,第i个数Ti表示第i种卡片的价格
第3行 N个正整数,表示卡片序列。
输出文件
1行 1个整数ans,表示boboo剩余的钱数,若不能集齐,输出’NO ans’,不含引号。
样例输入
5 2 20
10 5
1 1 2 2 1
样例输出
5
注释
【样例解释】
购买2-3 或者 4-5 都可,花费15,剩余钱数20-15=5.
【数据范围】
对于100%的数据 N<=1000000 ,M<=2000 ,Ti<=2000 , V<=10^9
对于30% 的数据 N<=2000


思路

这个题一看数据规模,就知道算法大概就是O(n)的了。
具体的思路就是,用h,t(h是头指针,t是尾指针)来维护一个队列。每当新加入一个元素,就更新种类数,然后判断价值是否大于v,如果大于,就要后移h,减少元素。
然后判断种类数k是否=m,,如果等于,那么就先看队列头部是否都是些重复元素,如果是就要删去只剩一个,因为删去这些不会减少种类数k,但是能减少花费。显然是靠拢最优解。

代码

var ans,sum,k,h,t,n,m,v,j,i:longint;    a:array[1..1000000]of longint;    cost:array[1..2000]of longint;    f:array[1..1000000]of longint;  //f是存数量begin  assign(input,'card.in');  assign(output,'card.out');  reset(input);  rewrite(output);  readln(n,m,v);  for i:=1 to m do read(cost[i]);  for i:=1 to n do read(a[i]);  f[a[1]]:=1;  sum:=cost[a[1]];  ans:=maxlongint;  k:=1;  h:=1;t:=1;  for i:=2 to n do    begin      inc(t);      inc(f[a[i]]);      if f[a[i]]=1 then inc(k);      inc(sum,cost[a[i]]);      while (sum>v)and(h<=t)do        begin          dec(f[a[h]]);          dec(sum,cost[a[h]]);          if f[a[h]]=0 then dec(k);          inc(h);        end;      if k=m then        begin          while (f[a[h]]>1)and(h<t) do            begin              dec(sum,cost[a[h]]);              dec(f[a[h]]);              inc(h);            end;          if ans>sum then ans:=sum;        end;    end;  if ans=maxlongint then write('NO ans')     else write(v-ans);  close(input);  close(output);end.
0 0
原创粉丝点击