01背包
二维这样写
for (int i=1;i<=m;i++) for (int j=w[i];j<=t;j++){ a[i][j]=a[i-1][j]; if (j-w[i]>=0)a[i][j]=max(a[i-1][j],a[i-1][j-w[i]]+v[i]);
因为很长时间没写,受一维的影响,一开始写成了这样
for (int i=1;i<=m;i++) for (int j=w[i];j<=t;j++) a[i][j]=max(a[i-1][j],a[i-1][j-w[i]]+v[i]);
需要注意的是,a[i][1~w[i]]没值啊没值啊没值啊(是栋栋发现的!栋栋太厉害了!)

for (int i=1;i<=m;i++) { for(int j=0;j<w[i];j++) a[i][j]=a[i-1][j]; for (int j=w[i];j<=t;j++) a[i][j]=max(a[i-1][j],a[i-1][j-w[i]]+v[i]); }
一维
j是从m循环到w[i](省去判断了,不用担心上面那个问题)
为什么倒着循环?
省略了第一维,在二维数组中,我们是由a[i-1][j-w[i]]更新a[i][j]的
a[j]=max(a[j],a[j-w[i]]+v[i]);
推a[j]需要用到a[j-w[i]],显然j-w[i]<j,如果正着循环,就相当于用a[i][j-w[i]]更新a[i][j]了
for (int i=1;i<=n;i++) for (int j=m;j>=w[i];j--) a[j]=max(a[j],a[j-w[i]]+v[i]);
如果要把背包装满呢?
那就把a[0]初始化为0,a[1~n]初始化为-∞。
a[0]相当于装满了容量为0的背包,价值为0,是合法状态。
a[1~n]全是不合法的状态。
在循环过程中,如果a[j-w[i]]是合法的状态,那么a[j]就是合法的状态。
(上面是小敏颜告诉我的)
而对于普通的背包,全部初始为0,就是全都合法。
完全背包
和01背包的区别是一种物品可以放无限次。所以刚才说的倒着循环,就没必要了。
因为既然可以放无限次,就不用考虑上一次到底是放还是没放了。
for (int i=1;i<=m;i++) for (int j=w[i];j<=t;j++) a[j]=max(a[j],a[j-w[i]]+v[i]);
多重背包
n种物品,每种有sum[i]件,价值为v[i],代价w[i],装到容量为m的包里使价值最大
拆成01背包
二进制拆分
xp[0]=1;for (int i=1;i<=14;i++) xp[i]=xp[i-1]*2;
这地方注意,xp[0]等于1。
for (int i=1;i<=n;i++){ for (int j=0;j<=14;j++){ if (num[i]<xp[j]) break; w[++cnt]=w1[i]*xp[j]; v[cnt]=v1[i]*xp[j]; num[i]-=xp[j]; } if (num[i]){ w[++cnt]=w1[i]*num[i];v[cnt]=v1[i]*num[i]; } }
w,v是新的数组,num[]是每个物品的件数,价值和代价要一起拆。
混合背包
就是把三种背包合起来,读入的时候判断一下件数。(完全背包最多是m/w[i])
然后拆。。。01背包做。
二维费用
数组加一维,循环加一重
像这样(我懒)(下边是个01背包,别的拆一拆。。。)
for (int i=1;i<=n;i++) for (int j=zl;j>=w[i];j--) for(int k=tj;k>=v[i];k--) a[j][k]=max(a[j][k],a[j-w[i]][k-v[i]]+V[i]);
分组背包
for 所有的组k for v=V..0 for 所有的i属于组k f[v]=max(f[v],f[v-c[i]]+w[i])
特别注意一下2、3重循环的顺序。这样才能保证每组内的物品只装一次。
多个背包问题(我也不知道叫啥)
有多个相同的背包,每个背包容量相同,向里边装入n个物品,求最大数量
用f[j][k] 表示装到第j个背包,第j个背包已经装了k个容量的最大数量。
对于第i个物品有两种选择,一是装进前面的背包中,二是自己新开一个背包
后边我就不会了……