集卡片

题目描述

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值