編程訓練——多重部分和問題

題目

給定n個不同大小的數字aia_i,每種數字個mim_i個,判斷是否可以從這些數字中選出若干數是的它們的和恰好爲K。

樣例輸入

3
3 5 8
3 2 2
17

這個樣例輸入的意思是一共有三種數字,分別是3、5、8,每個數字的個數分別是3、2、2.
即從3個3、2個5、2個8中是否能找出一個組合,其和爲17?

樣例輸出

Yes

3個3和1個8的和就是17,所以輸出Yes;反之輸出No。

算法

典型的動態規劃就能夠求解。

dp[i][j]dp[i][j]表示前i個數字(0i10~i-1號)是否能選出若干和爲K。

初始化爲dp[0][0]=1dp[0][0]=1dp[0][]=0dp[0][非零]=0,1代表Yes,0代表No。從前0個數中(也就是沒有數)能選出和爲0的數,但不能選出和爲非零的數。

dp[i+1][j]dp[i+1][j]就是所有dp[i][jka[i]]dp[i][j-k*a[i]]的值進行或運算(如果有一個爲1就爲1)。

遞推式直接用代碼表示:

for(int i = 0;i < n;i++){
    for(int j = 0;j <= K;j++){
        for(int k = 0;k <= m[i] && k * a[i] <= k;k++){
            dp[i+1][j] = dp[i][j-k*a[i]];
            if(dp[i+1][j] == 1)break;
        }
    }
}

代碼

#include<stdio.h>

#define maxn 105
#define maxK 100005

int main(){
    int n, K;
    int a[maxn], m[maxn];
    int dp[maxn][maxK];     // 嚴謹來講應該是maxn+1和MaxK+1,但我將maxn和maxK稍微定大了一點,所以沒關係
    while(scanf("%d", &n)==1){
        for(int i = 0;i < n;i++){
            scanf("%d", &a[i]);
        }
        for(int i = 0;i < n;i++){
            scanf("%d", &m[i]);
        }
        scanf("%d", &K);
        // 初始化dp
        for(int j = 1;j <= K;j++){
            dp[0][j] = 0;
        }
        dp[0][0] = 1;

        for(int i = 0;i < n;i++){
            for(int j = 0;j <= K;j++){
                for(int k = 0;k <= m[i] && k * a[i] <= k;k++){
                    dp[i+1][j] = dp[i][j-k*a[i]];
                    if(dp[i+1][j] == 1)break;
                }
            }
        }
        printf("%d\n", dp[n][K]);
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章