題目描述
Description
將整數n分成k份,且每份不能為空,任意兩種划分方案不能相同(不考慮順序)。
例如:n=7,k=3,下面三種划分方案被認為是相同的。
1 1 5
1 5 1
5 1 1
問有多少種不同的分法。
輸入描述
Input Description
輸入:n,k (6<n<=200,2<=k<=6)
輸出描述
Output Description
輸出:一個整數,即不同的分法。
樣例輸入
Sample Input
7 3
樣例輸出
Sample Output
4
//
解析:這一題實際上是組合數學里面的經典問題,跟第二類Stirling數有些相似。可以把一個數值為n的數看成n個小球,划分的份數k看作是k個盒子,那么本題的要求就是:
解析:這一題實際上是組合數學里面的經典問題,跟第二類Stirling數有些相似。可以把一個數值為n的數看成n個小球,划分的份數k看作是k個盒子,那么本題的要求就是:
將n個小球放到k個盒子中,小球之間與盒子之間沒有區別,並且最后的結果不允許空盒
與第二類Stirling數的遞推公式的推導過程相似:
將n個小球放到k個盒子中的情況總數=1、至少有一個盒子只有一個小球的情況數+2、沒有一個盒子只有一個小球的情況數
這樣進行划分的原因是這種分類足夠特殊,1和2都有可以寫出來的表達式:
1. 因為盒子不加區分,那么1的情況數與“將n-1個小球放到k-1個盒子中”的情況數一樣
2. 沒有一個盒子只有一個小球,那么把每個盒子中拿出來一個小球,對應的是“把(n-k)個小球放到k個盒子中的情況數”
至於1和2中的兩種等價關系為什么成立,可以用集合A=集合B的方式去證明
最后將上面的敘述轉化為dp的表達形式:
f[n][k]代表將n個小球放到k個盒子中且沒有空盒的情況,那么f[n][k] = f[n-1][k-1] + f[n-k][k]
(copy自大神)
代碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int dp[1000][1000];
int main()
{
int i,j,x,n,k;
cin>>n>>k;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
{
if(i==j) dp[i][j]=1;
else dp[i][j]=dp[i-j][j]+dp[i-1][j-1];
}
cout<<dp[n][k]<<endl;
return 0;
}