一些背包问题

35 篇文章 0 订阅
23 篇文章 0 订阅

备战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.
           



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值