题目描述
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.