[dp]poj1664 放蘋果 dp解法
阿新 • • 發佈:2019-01-02
放蘋果
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; } }