1. 程式人生 > >[dp]poj1664 放蘋果 dp解法

[dp]poj1664 放蘋果 dp解法

放蘋果
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 27747 Accepted: 17550

Description

把M個同樣的蘋果放在N個同樣的盤子裡,允許有的盤子空著不放,問共有多少種不同的分法?(用K表示)5,1,1和1,5,1 是同一種分法。

Input

第一行是測試資料的數目t(0 <= t <= 20)。以下每行均包含二個整數M和N,以空格分開。1<=M,N<=10。

Output

對輸入的每組資料M和N,用一行輸出相應的K。

Sample Input

1
7 3

Sample Output

8

這道題目除了用dp做以外,還可以用很多其他的方法過。因為題目資料非常小,各路大牛的部落格上有層出不窮的解法,我見過的有可以用DFS遞迴的方法,組合數學的方法,還有暴力打表法……今天來探討一下使用dp做的方法。

拿到題目一看,很容易想到用dp[i][j] 表示共i個蘋果放入j個盤子的方法。

狀態轉移的過程一看好像很多。不知如何構建,但是我們可以從目標狀態入手!把i個蘋果放入j個盤子裡的情況無非有兩種

1、這j個盤子中有空盤子,這時候i個蘋果放入j個盤子的方法和i個蘋果放入j-1個盤子的方法是一樣的,

得到dp[i][j] = dp[i][j-1] (當滿足i<j時,此情況必定成立。)

2、 j個盤子中沒有空盤子,那麼我們可以從每個盤子中移除一個,那麼原問題轉換成把i-j個蘋果放到j個盤子裡。

題目裡面很難理解的一點是,為什麼沒有空盤子時,蘋果只拿走一個?

這個問題我們可以這樣想,假設放置最少的盤子中有k個蘋果,那麼這個狀態可以唯一地,由連續k次每次從每個盤子中拿走一個蘋果而得到,所以就不會出現重複的子狀態,也就滿足了題目的顯示條件!

想清楚以後,還有邊界條件需要非常注意,dp[0][i] = 1,dp[1][i] = 1,dp[i][1] = 1;

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 15;
int dp[MAXN][MAXN];
int M,N;
// 定義dp[i][j]為把i個蘋果放在j個盤子裡的放法
//     dp[i][j] = dp[i][j-1] 或者
//     dp[i][j] = dp[i][j-1] +dp[i-j][j]
//
//     如果j個盤子中有空盤子,那麼就轉換成dp[i][j-1]
//     如果沒有空盤子,我們就先給這j個盤子放每個盤子放一個蘋果
//     轉換成dp[i-j][j]
//
int main()
{
    int T;
    cin>>T;

    while(cin>>M>>N)
    {
        memset(dp,-1,sizeof(dp));
        for(int i = 1; i<=10; i++)
            dp[0][i] = dp[1][i] = dp[i][1] = 1;
        for(int i = 1; i <= 10; i++)
        {
            for (int j =1; j<=10; j++)
            {
                if(dp[i][j] == -1)
                {
                    if (i<j) //證明有空盤子
                        dp[i][j] = dp[i][j-1];
                    else  //證明沒有空盤子,那就先給這j個盤子裡面每個
                        //都放一個蘋果,轉換成dp[i-j][j];
                        dp[i][j] = dp[i][j-1] +dp[i-j][j];
                }
            }
        }
        cout<<dp[M][N]<<endl;
    }
}