1. 程式人生 > >063_多重部分和問題(DP)

063_多重部分和問題(DP)

     多重部分和問題:給定一組數,並且給定期中每個數出現的次數,試問是否存在這樣的組合,使其和為一個值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;
    
}