這是一道求背包問題第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;}