备战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.
多个不同容量的背包,多个不同体积的物体(相同体积的物品很多)。
因为不知道取多少最优,所以迭代加深是可取的,一般的枚举深度太慢,所以我们二分深度检验。
此时已优化很多,但还是过不了,还有强剪枝。
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.