求01背包的第k大解
这是一道求背包问题第k次大解。思路稍有点变法,就是在递归的过程中,分别用两个数组记录两种状态(选择或不选择),并且只要记录前k次。在这两个数组中都是前k次可能的最优解。所以我们只要把这两个数组做比较,一直排到k就行了,另外,对于递归问题,我们只要抓住某个状态的前后关系,把复杂问题简单化,不必去纠结全过程的状态,应为程序在运行时,递归过程是用堆栈实现的,其变化复杂,没必要每步弄懂。重点在于抓住前后规律和设计思路就可以了。
#include<stdio.h>
#include<string.h>
int dp[1001][50],wi[100],di[100]; int n,m,k; void BK() { int a[50],b[50]; //用于记录当体积为j时,它的价值量。 int i,j,h; int x,y,z; for(i=0;i<n;i++) for(j=m;j>=wi[i];j--) { for(h=1;h<=k;h++) //把前k个一次比较排序,不必要全部排完。 { a[h]=dp[j-wi[i]][h]+di[i]; //选择两次状态,应为它的优解,只可能在这两者选择。 b[h]=dp[j][h]; //要么选择一个,要么不选择。 } x=y=z=1,a[h]=b[h]=-1; //从两个数组中把前k的值排序,此时h>K,就不必管他了,令它为-1是应为 while(z<=k&&(a[x]!=-1||b[y]!=-1)) // 在选的时候,以保证x,y不会超出k范围。 { //这个循环就是从前k个里面选出最优解(降序)。 if(a[x]>b[y]) dp[j][z]=a[x++]; else dp[j][z]=b[y++]; if(dp[j][z]!=dp[j][z-1]) z++; } } printf("%d\n",dp[m][k]); } int main() { int t,i; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&m,&k); for(i=0;i<n;i++) scanf("%d",&di[i]); for(i=0;i<n;i++) scanf("%d",&wi[i]); memset(dp,0,sizeof(dp)); BK(); } return 0; }