解题思路:对于01背包的状态转移方程式f[v]=max(f[v],f[v-c[i]+w[i]]);其实01背包记录了每一个装法的背包值,但是在01背包中我们通常求的是最优解,
即为取的是f[v],f[v-c[i]]+w[i]中的最大值,但是现在要求第k大的值,我们就分别用两个数组保留f[v]的前k个值,f[v-c[i]]+w[i]的前k个值,再将这两个数组合并,取第k名。
即f的数组会增加一维。
http://blog.csdn.net/lulipeng_cpp/article/details/7584981这个讲得很详细
反思:01背包没有理解,即分别用两个数组去存放f[v],f[v-c[i]]+w[i]的前k个值时,这k个值就是有序的,所以合并起来也是有序的,至于为什么是有序的,可以再看这个状态转移方程
for(i=1;i<=n;i++)
{
for(j=v;j>=c[i];j--)
f[v]=max(f[v],f[v-c[i]+w[i]]);//此时包的价值取决于上一个包有没有放进去的决策,不管那个包有没有放进去,当前状态的f[v]都是这两个值的最大值,所以
从1--v,f[v]是递增的。
}
用一个简单的例子来模拟一下
有一个容量为10的包,现在有3件物品,
重量 价值
3 4
4 5
5 6
f[j] | j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
i | 1 | 0 | 0 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
2 | 0 | 0 | 4 | 5 | 5 | 5 | 9 | 9 | 9 | 9 | |
3 | 0 | 0 | 4 | 5 | 6 | 6 | 6 | 6 | 11 | 11 | |
可以看到当j的取值从1到n的时候,f[v]的值是递增的,
#include<stdio.h> int c[1010],w[1010]; int main() { int ncase,n,v,k,i,j,x,y,z,t; scanf("%d",&ncase); while(ncase--) { int f[1010][50]={0}; int a[50],b[50]; scanf("%d %d %d",&n,&v,&k); for(i=1;i<=n;i++) scanf("%d",&w[i]); for(i=1;i<=n;i++) scanf("%d",&c[i]); for(i=1;i<=n;i++) { for(j=v;j>=c[i];j--) { for(t=1;t<=k;t++) { a[t]=f[j-c[i]][t]+w[i]; b[t]=f[j][t]; } x=y=z=1; a[t]=b[t]=-1; while(z<=k&&(x<=k||y<=k)) { if(a[x]>b[y]) f[j][z]=a[x++]; else f[j][z]=b[y++]; if(f[j][z]!=f[j][z-1]) z++; } } } printf("%d\n",f[v][k]); } }