【题目描述】
有一个容量为W的背包,有N种物品,第i种物品的体积为Ti,价值为Vi。
对于每种物品,你可以选择是否将它放入背包,放入背包的物品的总体积不能超过W,在这个前提下你希望放入背包的物品的总价值最大。
【输入数据】
第一行两个数W,N,表示背包的容量和物品的数量。
之后N行,每行两个数Ti,Vi,表示第i件物品的体积和价值。
【输出数据】
输出一个数,放入背包的物品的最大总价值。
【样例】
bag.in |
bag.out |
9 3 7 14 5 9 4 7 |
16 |
【数据规模】
前边30%的数据: n≤20 w≤100000000 3个数据;
中间40%的数据: n≤300 w≤100000000 6个数据;
后面30%的数据: 300<n≤1000 w≤10000 3个数据。
【数据提示】
数据保证随机。
题目分析:
题意简单,好像是动规中的01背包问题。
01背包的时间复杂度O(nV)或者O(nw),这样好像只能得30分。
怎么办呢?题目中好像只有n比较小,能不能想办法从n下手?
没有别的办法就搜吧,注意加上搜索的优化和卡时。
首先预处理数据
(1)按性价比从高到底的顺序对物品排序;
(2)贪心求出最大值;
(3)求后i项的价值总和,搜索优化用;
(4)求后i项最小的体积,搜索优化用。
搜索:
为了防止超时,要记录搜索运行的次数,当次数<=50000000时,可以继续搜,否则果断结束,输出当前结果
var v,w,sumv,minw:array[1..1000]of int64; f:array[0..10000]of int64; m,n,i,j,ans,nowmax,tot:longint;//time,ttime:double; procedure init; var i:longint; begin assign(input,'bag.in');reset(input); assign(output,'bag.out');rewrite(output); readln(m,n); for i:=1 to n do begin readln(w[i],v[i]); end; end; procedure qsort(l,r:longint);//按性价比从高到低排序 var i,j,t:longint;x:double; begin i:=l;j:=r;x:=v[(l+r) shr 1]/w[(l+r) shr 1]; repeat while v[i]/w[i]>x+1e-7 do inc(i); while v[j]/w[j]<x-1e-7 do dec(j); if i<=j then begin t:=w[i];w[i]:=w[j];w[j]:=t;t:=v[i];v[i]:=v[j];v[j]:=t; inc(i);dec(j); end; until i>j; if j>l then qsort(l,j);if i<r then qsort(i,r); end; function min(a,b:longint):longint; begin if a<b then exit(a) else exit(b) end; function max(a,b:longint):longint; begin if a>b then exit(a) else exit(b) end; procedure dfs_predeal; var i,t:longint; begin qsort(1,n); i:=0;t:=m;ans:=0;nowmax:=0;tot:=0; for i:=1 to n do if w[i]<=t then begin dec(t,w[i]);inc(ans,v[i]); end;//贪心法求得最大值 for i:=n downto 1 do sumv[i]:=sumv[i+1]+v[i];{//求后i项的价值总和,搜索优化用} minw[n+1]:=maxlongint; for i:=n downto 1 do minw[i]:=min(minw[i+1],w[i]);{求后i项最小的体积,搜索优化用} end; procedure dfs(i,mm:longint);//考虑第i件物品,当前剩余体积为mm begin if minw[i]>mm then begin//如果后i件物品中最小的体积都大于mm if nowmax>ans then ans:=nowmax; exit; end; if nowmax+sumv[i]<=ans then exit;{如果加上剩余i件物品的价值总和,还小于当前的最优值} inc(tot);if tot>50000000 then begin writeln(ans);close(output);halt; end; if mm>=w[i] then begin inc(nowmax,v[i]);//选第i件物品 dfs(i+1,mm-w[i]); dec(nowmax,v[i]);//回溯 end; dfs(i+1,mm); end; procedure dp; begin fillchar(f,sizeof(f),0); for i:=1 to n do for j:=m downto w[i] do f[j]:=max(f[j],f[j-w[i]]+v[i]); ans:=f[m]; end; begin init; if (m>10000) then begin dfs_predeal; dfs(1,m); end else dp; writeln(ans); close(output); end.