1. 题目
poj 3093
2. 题目实质
给定一个01背包和n个物品,求有多少种选择方法使得背包再也放不下余下的任意物品。
3. 算法
预处理:按照物品体积排序。
首先,枚举第一个有可能装不下的物品(这里从大到小枚举,可以省去求和的时间),这样一来,他之前的物品一定都能装下,从背包的总体积中减去。
然后再对可能装不下的物品(比枚举的这个物品大的)做一下改版的 01 背包,也就是 01 背包求方案数:那么 i 为最小进不了物品时,对应的方案数就是:以比 i 大的物品做一个容积为(背包容积 - sum(<i) )的 01 背包,使得剩余的容积不足 Vi 的方案数。
4. 注意事项
搜索的全部超时去吧......这是个动规。
5. 代码
改版 01 背包 (SueMiller)
program ACRush;
var ca,cas:longint;
n,m:longint;
a,f:array[0..1001]of longint;
i,j,k:longint;
ans,sum:longint;
procedure quicksort(l,r:longint);
var x,y,i,j:longint;
begin
i:=l; j:=r; x:=a[(l+r)>>1];
repeat
while a[i]<x do inc(i);
while x<a[j] do dec(j);
if i<=j then
begin
y:=a[i]; a[i]:=a[j]; a[j]:=y;
inc(i); dec(j);
end;
until i>j;
if l<j then quicksort(l,j);
if i<r then quicksort(i,r);
end;
function max(a,b:longint):longint;
begin
if a>b then exit(a) else exit(b);
end;
begin
readln(cas);
for ca:=1 to cas do
begin
fillchar(a,sizeof(a),0);
readln(n,m);
sum:=0;
for i:=1 to n do
begin
read(a[i]);
sum:=sum+a[i];
end;
readln;
if sum<=m then
begin
writeln(ca,' ',1);
continue;
end;
if a[1]>m then
begin
writeln(ca,' ',0);
continue;
end;
quicksort(1,n);
ans:=0;
fillchar(f,sizeof(f),0);
f[0]:=1;
//---------------------这是核心部分----------------------
for i:=n downto 1 do
begin
sum:=sum-a[i];
for j:=max(m-sum-a[i]+1,0) to m-sum do
ans:=ans+f[j];
for j:=m downto a[i] do
f[j]:=f[j-a[i]]+f[j];
end;
//-------------------------------------------------------------
writeln(ca,' ',ans);
end;
end.
(* 其实我觉得背包类动规是个好神奇的东西,像这个 01 背包,我一直感觉着答案递推不出来,只能用递归做,但事实上它确实是出来了。所以呢,这方程,我还是生背的。*)
//本博客有置顶 dd_engi 的背包九讲。