動態規劃題解 D003 放蘋果
題目解讀
原題連結:牛客網 北京大學歷年考研複試機試專題
題目描述
把 M 個同樣的蘋果放在 N 個同樣的盤子裡,允許有的盤子空著不放,問共有多少種不同的分法?
注意:5、1、1 和 1、5、1 是同一種分法,即順序無關;
輸入描述
輸入包含多組資料,每組資料包含兩個正整數 m和n(1≤m, n≤20)
輸出描述:
對應每組資料,輸出一個整數k,表示有k種不同的分法。
示例1
輸入
7 3
輸出
8
題意理解
將M個蘋果放到N個同樣的盤子中,允許有空盤子不放。脫離實際問題進行分析,這實際上就對應於一個整數劃分的問題。用數學語言進行描述,即有:給定一個整數M,將它分解為集合 { a1 , a2 , a3……ak },要求集合中的元素滿足a1+a2+a3+……+ak=M,且元素總個數<=N,求有多少種不同的集合?
演算法分析
依照通常動態規劃問題的思路,考慮這樣一個數組dp[m][n] , 表示在有m個蘋果,n個盤子時總的分法數量。
先考慮這樣一種情況:m < n時,蘋果的數量小於盤子的數量。在這種情況下,即使按照最大程度使用盤子,即每個盤子中只放一個蘋果,仍然只能使用m個盤子,則剩下n-m個盤子必然是空的,這些盤子對於實際的劃分方案沒有影響。因為集合中元素不考慮排序的問題,所以也不需要考慮哪m-m個盤子為空,這個時候dp[m][n] 就退化為 dp[m][m] 。
第二種情況下有m>=n, 蘋果的數量大於等於盤子的數量。我們接下來的劃分標準變為是否含有空盤子。如果沒有空盤子,我們可以先向每個盤子中分配一個蘋果,這樣就保證了所有的盤子均不為空 ; 如果至少有一個空盤子,則將這個空盤子給減出來;綜上可知,在這第二種情況中的狀態轉移方程為:
dp[m][n] = dp[m-n][n] + dp[m][n-1] ;
至於這裡為什麼只加了一個dp[m][n-1],而沒有加dp[m][n-2]……這是因為對於dp[m][n-1]中實際上已經包含了對m放到n-1個盤子中的各種情況,也包括只是用n-2個盤子,那對於此時dp[m][n]而言,相當於有兩個空盤子沒被使用。其他空盤子數量的時候同理可得。
注意點
對於初始化的時候,應該考慮:
任意數量的蘋果,0個盤子或者1個盤子,方案數量均為1 ;
任意數量的盤子,0個蘋果或者1個蘋果,方案數量均為1 ;
程式碼
#include<stdio.h>
#include<string.h>
#define MAXNUM 21
using namespace std ;
int n,m;
void Search()
{
int dp[MAXNUM][MAXNUM];
memset(&dp,0,sizeof(dp));
for(int i=0;i<=n;i++){
dp[0 ][i]=1;
dp[1][i]=1;
}
for(int i=0;i<=m;i++){
dp[i][0]=1;
dp[i][1]=1;
}
for(int i=2;i<=m;i++){
for(int j=2;j<=n;j++){
if(i<j){
dp[i][j] = dp[i][i];
}
else{
dp[i][j] = dp[i-j][j]+dp[i][j-1];
}
}
}
printf("%d\n",dp[m][n]);
return ;
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF){
Search();
}
return 0 ;
}