1. 程式人生 > >sincerit 數字和為sum的方法數(01揹包問題)

sincerit 數字和為sum的方法數(01揹包問題)

題目描述
給定一個有n個正整數的陣列A和一個整數sum,求選擇陣列A中部分數字和為sum的方案數。
當兩種選取方案有一個數字的下標不一樣,我們就認為是不同的組成方案。
輸入描述:
輸入為兩行:
第一行為兩個正整數n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行為n個正整數Ai,以空格隔開。
輸出描述:
輸出所求的方案數
示例1
輸入
複製
5 15 5 5 10 2 3
輸出
複製
4

好久沒做揹包問題了趕緊看一遍優化原理:
https://blog.csdn.net/witnessai1/article/details/52702754

01揹包問題優化後的列舉是要從大到小的,因為我們使用的是一維陣列
在求dp[v]得時候要使用到以前得資料 即dp[i][v] = max(dp[i-1][v], dp[i-1][v-weight[i]]+cost[i]);
當要求前i個物體揹包容量為v時的最大價值,就要用到dp[i-1][v], dp[i-1][v-weight[i]]+cost[i]
也就是在一維陣列時求dp[v]要用到dp[v]和dp[v-weight[i]]這兩個是i-1層的資料
只能從大到小列舉的原因是:防止dp[v]覆蓋i-1層的資料也就是dp[i-1][v]的資料
假設從小到大,那麼求dp[v]時, dp[v] = max(dp[v], dp[v-weight[i]]+cost[i])
顯然現在求得的dp[v]已經是dp[i][v]了, 可是當後面要求的資料要使用dp[i-1][v]的時候(因為有v-weight[i]這個值比v要小),就不能再用dp[v]因為dp[i-1][v]的值被dp[i][v]覆蓋了
因此只能從大到小列舉。

#include <stdio.h>
#include <cstring>
long long dp[1005], A[1005];
// 01揹包問題 
// dp[i]表示以和為i的方案數
// 對於A[i] dp[j] = dp[j] + dp[j-A[i]];
// 初始值dp[0] = 1 A[i]=5剛好能湊起的時候 dp[5-5] = 1; 剛好一個5能得到和為5的方案數  
int main() {
  int n, sum;
  scanf("%d %d", &n, &sum);
  for (int i = 0; i < n; i++) scanf("%lld"
, &A[i]); memset(dp, 0, sizeof(dp)); dp[0] = 1; for (int i = 0; i < n; i++) { for (int j = sum; j >= A[i]; j--) { dp[j] = dp[j] + dp[j - A[i]]; } } printf("%lld\n", dp[sum]); return 0; }