1. 程式人生 > >動態規劃題解 D003 放蘋果

動態規劃題解 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 ; }