063_多重部分和問題(DP)
阿新 • • 發佈:2019-01-31
多重部分和問題:給定一組數,並且給定期中每個數出現的次數,試問是否存在這樣的組合,使其和為一個值K?(某個數可以取多次,但不能超過其次數)
如:a={3,5,8}
m={3,2,2}
K=17;
即 數字3有3個,數字5有2個,數字8有2個。是否存在和為17的組合?
就此情況而言,存在3*3+8=17這樣的組合,所以輸出應該為Yes。
與完全揹包類似,應該避免三重迴圈。
為解決此類問題,給出如下定義:
dp[i+1][j]=只用前i種物品加和為j時,第i種物品最多能剩餘的個數(不能得到和為j時為-1)
所以其遞推的過程如下:
1. 先判斷dp[i][j]是否大於等於0,如果滿足條件,說明根本不用第i號物品就能滿足要求。所以,根據定義,第i號物品應該一個都不要用,此時:
dp[i+1][j]=m[i];
2.如果不滿足上述情況,那麼(1)如果j<a[i],那麼肯定不能得到和為j;
或者 (2)dp[i+1][j-a(i)]<=0,說明第i號物品已經被用完,所以和可定得不到為j;
dp[i+1][j]=-1;
3,如果並沒有落入2的情況,那麼就還可以得到如下結果:
dp[i+1][j]=dp[i+1[j-a(i)]-1;
程式設計時注意初始化的問題:首先將dp陣列都初始化為-1,若j=0時,什麼物品都不要用就可達到要求,所以dp[i+1][0]=m[i];
// // 063_multi sum.cpp // changlle // // Created by user on 12/28/15. // Copyright (c) 2015 user. All rights reserved. // #include <iostream> using namespace std; int n=3; int a[3]={3,5,8}; int m[3]={3,2,2}; int K=17; int main () { int dp[4][18]; memset(dp,-1,sizeof(dp)); for (int i=0;i<=n;i++) dp[i+1][0]=m[i]; for (int i=0;i<n;i++) for (int j=1;j<=K;j++) { if (dp[i][j]>=0) dp[i+1][j]=m[i]; else if (j<a[i] || dp[i+1][j-a[i]]<0) dp[i+1][j]=-1; else dp[i+1][j]=dp[i+1][j-a[i]]-1; } for (int i=0;i<=n;i++) { for (int j=0;j<=K;j++) cout<<dp[i][j]<<" "; cout<<endl; } if (dp[n][K]>=0) cout<<"Yes"<<endl; else cout<<"No"<<endl; return 0; }