【NOIP动态规划专题】采药2

【NOIP动态规划专题】采药2

Time Limits: 1000 ms Memory Limits: 131072 KB

Description
  辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”   如果你是辰辰,你能完成这个任务吗?

Input
  输入文件的第一行包含两个正整数N,M。M表示总共能够用来采药的时间,N代表山洞里的草药的数目。接下来的N行每行包括两个的整数,分别表示采摘某株草药的时间Ti和这株草药的价值Vi。

Output
  输出文件仅包含一个整数表示规定时间内可以采到的草药的最大总价值。

Sample Input

3 9 10 10 8 1 1 2

Sample Output

3

Data Constraint

50%的数据中 N,M ≤ 1000;
100%的数据中 N,M ≤ 100000,Ti,Vi ≤10。


解题思路

这道题一看就是背包问题,第一反应就是01背包,O(nm),当自己感觉胜算在握的时候,突然发现,n,m<=100000,于是n×m=10000000000,看起来不会爆,只是评测机会炸掉罢了……

当灰心丧气的时候,转眼一看Ti,Vi<=10,立即地可以想到这直接分成121种背包,令d[x,y]表示花费为x收益为y的背包个数,咦?!多重背包?!O(121mk)加起来差不多是O(nm)了!!!!!哭死23333333333333………………

隐隐约约想起昨天刚刚学到的单调队列——立即有了思路

单调队列优化多重背包!!好了这题切了!!

Codes:

var
    f:array[0..1,0..100010]of longint;
    d:array[0..10,0..10]of longint;
    e:array[1..100010,1..2]of longint;
    i,j,k,l,n,m,x,y,last,now,ans,h,t:longint;

function max(x,y:longint):longint;
begin
    if x>y then exit(x) else exit(y);
end;

function min(x,y:longint):longint;
begin
    if xthen exit(x) else exit(y);
end;

procedure get(x,y,z:longint);
begin
    while(h<=t)and(z-e[h,2]>x*d[x,y])do inc(h);
    if h>t then exit;
    f[now,z]:=max(f[now,z],e[h,1]+y*((z-e[h,2])div x));
end;

procedure put(v,x,y,z:longint);
begin
    while(h<=t)and((e[t,1]+((z-e[t,2]) div x)*y)<=v)do dec(t);
    inc(t);
    e[t,1]:=v;
    e[t,2]:=z;
end;

begin
    read(n,m);
    for i:=1 to n do
    begin
        read(x,y);
        inc(d[x,y]);
    end;
    fillchar(f,sizeof(f),$ff);
    f[0,0]:=0;
    last:=1;now:=0;
    y:=min(10,m);
    for i:=0 to y do
        for j:=0 to 10 do
        begin
            if d[i,j]=0 then continue;
            if j=0 then continue;
            if i=0 then
            begin
                for k:=0 to m do inc(f[now,k],d[i,j]*j);
                continue;
            end;
            last:=1-last;
            now:=1-now;
            for k:=0 to i-1 do
            begin
                h:=1;t:=0;
                for l:=0 to (m-k)div i do
                begin
                    x:=l*i+k;
                    f[now,x]:=f[last,x];
                    if x>=i then get(i,j,x);
                    if f[now,x]>ans then ans:=f[now,x];
                    if f[last,x]>=0 then put(f[last,x],i,j,x);
                end;
            end;
        end;
    writeln(ans);
end.

你可能感兴趣的:(单调队列,DP)