备战noip ing
背包问题其实算np问题,一般的动规算是伪多项式。
按时间顺序来吧、、、、
1、lmd的搜索匹配
在n个数中选若干个数,使其和等于某个数。(n<=50)
据他自己说,数据范围和有特点、、、、正解的数据范围是暴力的两倍、、、、
结果几乎没人看出来。
容易看出2^n的枚举,但是无疑会超,于是我们劈成两半,先搜前一半,哈希一下,再搜另一半,搜完后,在哈希表中找,这样2^50降成了2^25。(hash不能用二分代替,lmd即卡了空间,又卡了时间)
(程序懒得找了)
2、多人背包
因为多人背包中,背包容量相同,所以实际上是普通背包问题的前k优解。
用队列当前状态的前k优解,每次更新时归并一下。
时间o(n^2*k)
var ans,n,k,maxv:longint;
f:array[0..5000,0..50]of longint;
g:array[0..50]of longint;
v,w:array[0..200]of longint;
procedure inf;
begin
assign(input,'back.in');reset(input);
assign(output,'back.out');rewrite(output)
end;
procedure ouf;
begin
close(input);close(output)
end;
function max(i,j:longint;var l1,l2:longint ): longint;
begin
if f[j-v[i],l1]+w[i]>f[j,l2] then begin
max:=f[j-v[i],l1]+w[i];inc(l1)
end
else begin
max:=f[j,l2];inc(l2)
end
end;
procedure updata(i,j:longint);
var l1,l2:longint;
begin
if f[j-v[i],0]<=0 then exit;
l1:=1;l2:=1;
fillchar(g,sizeof(g),129);g[0]:=0;
while (l1<=f[j-v[i],0])or(l2<=f[j,0]) do begin
inc(g[0]);g[g[0]]:=max(i,j,l1,l2);
if g[0]=k then break
end;
while g[g[0]]<0 do dec(g[0]);
f[j]:=g
end;
procedure init;
var i,j,sum:longint;
begin
readln(k,maxv,n);
for i:=1 to n do readln(v[i],w[i]);
fillchar(f,sizeof(f),129);
f[0,0]:=1;f[0,1]:=0;
for i:=1 to n do
for j:=maxv downto v[i] do
updata(i,j);
ans:=0;
i:=maxv;
sum:=0;
if f[i,0]=k then
for j:=1 to f[i,0] do sum:=sum+f[i,j];
if sum>ans then ans:=sum;
writeln(ans)
end;
begin
inf;
init;
ouf
end.
3、lzn的二分+迭代加深*+强剪枝搜索。
多个不同容量的背包,多个不同体积的物体(相同体积的物品很多)。
因为不知道取多少最优,所以迭代加深是可取的,一般的枚举深度太慢,所以我们二分深度检验。
此时已优化很多,但还是过不了,还有强剪枝。
1、升序排序,取尽量小的物品更优,但枚举时从大体积物品开始。
2、若当前物品容量>背包,无解。
3、因为此题同样体积物品很多,所以枚举背包时,可由与它体积相同的物品进入的背包开始枚,因为体积相同的物品是等价的。
4、若当前背包中无用体积+总需要放入的物品体积>背包总体积则无解。
var a,b,s,max:array[0..2000]of longint;
l,r,mid,tmp,n,m,tot,tot2,sum:longint;
procedure inf;
begin
assign(input,'star.in');reset(input);
assign(output,'star.out');rewrite(output)
end;
procedure ouf;
begin
close(input);close(output)
end;
function dfs(x,k:longint):boolean;
var tmp1,i,kk:longint;
begin
if x=0 then exit(true);
if sum<tot2 then exit(false);
if tmp+s[mid]>tot then exit(false);
dfs:=false;
if (b[x]=b[x+1]) then k:=max[x+1] else k:=1;
for i:=k to n do
if (b[x]<=a[i]) then begin
a[i]:=a[i]-b[x];sum:=sum-b[x];tot2:=tot2-b[x];max[x]:=i;
tmp1:=tmp;
if a[i]<b[1] then tmp:=tmp+a[i];
dfs:=dfs(x-1,k);
a[i]:=a[i]+b[x];sum:=sum+b[x];tot2:=tot2+b[x];max[x]:=0;
tmp:=tmp1;
if dfs then exit;
end;
end;
procedure qsort(l,r:longint);
var i,j,x,c:longint;
begin
i:=l;j:=r;x:=b[(l+r)>>1];
repeat
while b[i]<x do inc(i);
while x<b[j] do dec(j);
if not(i>j) then begin
c:=b[i];b[i]:=b[j];b[j]:=c;
inc(i);dec(j)
end
until i>j;
if i<r then qsort(i,r);
if l<j then qsort(l,j)
end;
procedure qsort2(l,r:longint);
var i,j,x,c: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 not(i>j) then begin
c:=a[i];a[i]:=a[j];a[j]:=c;
inc(i);dec(j)
end
until i>j;
if i<r then qsort2(i,r);
if l<j then qsort2(l,j)
end;
procedure init;
var i:longint;
ch:boolean;
begin
readln(n);
tot:=0;
for i:=1 to n do begin
readln(a[i]);
tot:=tot+a[i]
end;
readln(m);
for i:=1 to m do begin
readln(b[i]);
end;
qsort2(1,n);
qsort(1,m);
fillchar(s,sizeof(s),0);fillchar(max,sizeof(max),0);
for i:=1 to m do s[i]:=s[i-1]+b[i];
l:=0;r:=m;
while l<=r do begin
mid:=(l+r)>>1;sum:=tot;tot2:=s[mid];tmp:=0;
max[mid+1]:=1;
ch:=dfs(mid,1);
max[mid+1]:=0;
if ch then l:=mid+1 else r:=mid-1
end;
writeln(r)
end;
begin
inf;
init;
ouf
end.
4、roosephu的基于进制的贪心
基本同lzn,但是物体和背包数量达到10^5,容量达到10^9,唯一的特殊之处在于任意两件物品互为倍数关系。
如此的数据范围,搜索不可能,只能贪心。
因为任意两件物品互为倍数关系,所以我们假设物品按体积升序为:x1,x2,x3,x4,x5,x6....
那么任意背包可被唯一表示成k1*x1+k2*x2+k3*x3+k4*x4.....(满下一位则进位)。
将所有背包每一位统计(因为背包的划分使他们的体积可失去界限的使用)
我们再以物体体积由小到大在k中取,若自己这一位的k用完,则取下一位,尽量把小的体积的物品取完。
var a,c,d,b:array[0..500000]of longint;
n,m,ans:longint;
procedure inf;
begin
assign(input,'rumor.in');reset(input);
assign(output,'rumor.out');rewrite(output)
end;
procedure ouf;
begin
close(input);close(output)
end;
procedure qsort(l,r:longint);
var i,j,x,c:longint;
begin
i:=l;j:=r;x:=b[(l+r)>>1];
repeat
while b[i]<x do inc(i);
while x<b[j] do dec(j);
if not(i>j) then begin
c:=b[i];b[i]:=b[j];b[j]:=c;
inc(i);dec(j)
end
until i>j;
if i<r then qsort(i,r);
if l<j then qsort(l,j)
end;
procedure origin;
var i,x,j:longint;
begin
qsort(1,n);
for i:=1 to n do
if b[i]<>b[i-1] then begin
inc(d[0]);d[d[0]]:=b[i]
end;
for i:=1 to m do begin
x:=a[i];j:=d[0];
while x>d[1] do begin
c[j]:=x div d[j];
x:=x mod d[j]
end;
end;
end;
procedure init;
var i,j,x,cos,k:longint;
begin
readln(m,n);
for i:=1 to m do read(a[i]);readln;
for i:=1 to n do read(b[i]);readln;
fillchar(c,sizeof(c),0);
origin;
ans:=0;
for i:=1 to d[0] do begin
x:=d[i];
if c[i]>=x then begin inc(ans,x);dec(c[i],x) end
else begin
j:=i;k:=1;
while (j<=d[0])and(x>=c[j]*k) do begin
ans:=ans+c[j]*k;
x:=x-c[j]*k;
c[j]:=0;
inc(j);
k:=d[j] div d[i]
end;
if j<=d[0] then begin
ans:=ans+x;
cos:=x div k;
if x mod k<>0 then inc(cos);
c[j]:=c[j]-cos;
end
end
end;
writeln(ans)
end;
begin
inf;
init;
ouf
end.