[dp]多重部分和問題
阿新 • • 發佈:2019-01-31
多重部分和問題:
有n種不同大小的數字ai,每種各有mi個,判斷是否可以從這些數字中選出若干使它們的和恰好為k。
限制條件:
1 <= n <= 100
1 <= ai,mi <= 100000
a <= K <= 100000
樣例輸入:
n = 3
a = {3, 5, 8}
m= {3, 2, 2}
K=17
輸出:
Yes (3*3+8=17)
這個問題可以用dp求解,不過如何定義遞推關係會影響到最終的複雜度。首先我們看一下如下的定義:
dp[i+1][j] = 用前i種數字能否加和成j
為了用前i種數字加和成j,也就需要能用前i-1種數字加和成 j, j-ai, ……,j-mi*ai 中的某一種。由此我們可以定義如下遞推關係:
dp[i+1][j] = 0 (k<=k<=mi 且 k*ai <= j 時存在使dp[i][j-k*ai]為真的k)
int n; //數列的長度 int K; //目標的和數 int a[MAXN]; //數字值 int m[MAXN]; //每種數字的個數 int dp[MAXN][MAXN];//dp陣列 void solve() { dp[0][0] = true; for (int i = 0; i < n; i++) for(int j = 0; j <= K; j++) for(int k = 0; k<=m[i] && k*a[i]<=j;i++) dp[i+1][j] = dp[i][j-k*a[i]] | dp[i+1][j]; if (dp[n][K])printf("yes\n"); }
從複雜度上來說,這個演算法的複雜度是(KΣimi),並不夠好。一般用dp求bool結果的話會有不少浪費,同樣的複雜度通常能獲得更多資訊。在這個問題中,我們不光求出能否得到目標的和數,同時把得到ai時這個數還剩下多少個可以使用給計算出來,這樣就可以減少複雜度。