【笔记+代码】背包问题

来源:互联网 发布:创维网络电视怎么样 编辑:程序博客网 时间:2024/05/21 06:33

  • 01背包
    • 板子
    • 超大01背包
  • 完全背包
  • 多重背包
    • 二进制拆分
  • 混合背包
  • 二维费用背包
  • 分组背包
  • 有依赖背包问题

n个物品 背包容量W
w[i] 费用,v[i] 价值;

01背包

每件物品选或不选。

板子:

void zerone(int wei,int val){    for(int j=W;j>=wei;j--) d[j]=max(d[j],d[j-wei]+val);}for(int i=1;i<=n;i++)    zerone(w[i],v[i])

超大01背包

每个物品只能拿一次,物品数量M<=100,背包容量T<=10^9,每个物品的价值<=100,每个物品的重量<=10^9
将dp数组第二维变为价值,存储达到价值j所需的最小重量

dp[i][j]=min(dp[i][j],dp[i-1][j-v[i]]+w[i])

在dp[n][j]<=W里找最大的j

完全背包

每件物品可以选无数次。

void wanquan(int wei,int val){    for(int j=wei;j<=W;j++) d[j]=max(d[j],d[j-wei]+val);}for(int i=1;i<=n;i++)    wanquan(w[i],v[i])
数竞大佬的证明思路①dp[i][j]max=dp[i-1][j-k*w[i]]+k*v[i]  0<=k②dp[i][j]max=dp[i-1][j-w[i]-t*w[i]]+t*v[i]+v[i] 0<=t,1<=k            dp[i-1][j]                          k==0③dp[i][j-w[i]]max=dp[i-1][j-w[i]-t*w[i]]+t*v[i] 0<=t将③带入②dp[i][j]max = dp[i-1][j]            dp[i][j-w[i]]+v[i]

多重背包

每件物品可以选固定数量次。

二进制拆分

void duochong(int wei,int val,int num){    if(num*wei>=W) {        wanquan(wei,val);        return;    }    int k=1;    while(k<num){        zerone(wei*k,val*k);        num-=k;        k*=2;    }    zero(wei*num,val*num);    return ;}for(int i=1;i<=n;i++)    duochong(w[i],v[i],m[i])

时间复杂度 m*xigema(logk)
二进制拆分的可行性证明(不严格):
k=2^0+2^1+2^2+………2^x+p;
k=(111…….1)(2进制)+p;
那么从0–1111111….1的数,可由2的次幂的选否表示出来。
p<11111…..11(p<2^(x+1)),p+111…111=k;
从11111…..1+1—–k,固定选p,那么要凑的质量-p一定小于1111..1,
有上述结论可知,这个数是可以凑出来的。
证毕;

混合背包

每个物品可以选一件,无数件或给定数量件
解法:对于每件物品,判断是01,完全,多重,分别计算。

for(int i=1;i<=n;i++){    if(wanquan) wanquan(w[i],v[i]);    if(01) zerone(w[i],v[i]);    if(duochong) duochong(w[i],v[i],m[i]);}

二维费用背包

对于每个物品,有两种费用,每个费用都有最大值;
隐含方式:
最多取m件,这是件数就是另一个费用,每个物品此费用为1

for(int i=1;i<=n;i++){    for(int j=W;j>=w[i];j--){        for(int k=U;k>=u[i];k--){//另一个价值u            dp[j][k]=max(dp[j][k],dp[j-w[i]][k-u[i]]+v[i]);         }    }}完全jk顺序循环,多重拆分;

分组背包

所有物品分为k组,每组中至多选择一个物品;

for(int i=1;i<=k;i++) //每组内部     for(int j=W;j>=0;j--)//背包总重量         for(p)//p是该组内部的物品             dp[j]=max(dp[j],dp[j-w[p]]+v[p]);

正确性证明:
一开始以为只要背过代码就可以,结果度娘一搜,发现背包九讲第一版中居然有错误。

http://www.cppblog.com/Onway/archive/2010/08/09/122695.html
%这位不知名大神。

递推式dp[j]=max(dp[j],dp[j-w]+v]
循环顺序 组别,质量,组内物品。
对于每个组内,对每一个质量进行循环,由于j是逆序,dp[j]是上一组决策的结果。
同样由于j是逆序,质量为j-w的背包还未在(第一层循环)本组中决策,故j-w也是上一组决策的结果,保证滚动数组的正确性。

有依赖背包问题

原创粉丝点击