问题描述
将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5; 1,5,1; 5,1,1;
问有多少种不同的分法。
输入格式
n,k
输出格式
一个整数,即不同的分法
样例输入
7 3
样例输出
4 {四种分法为:1,1,5;1,2,4;1,3,3;2,2,3;}
数据规模和约定
6<n<=200,2<=k<=6
解题思路:本题最重要的是对状态转移方程的推导,下面提供两种思路推出dp[i]j。
思路一: 可以分为两种情况
(1)第j堆本来什么也没有,但是题目要求每一堆不能为空,所以第j堆放一个苹果(之后始终保持一个),所以还剩下i-1个苹果要分为j-1堆,即dp[i-1][j-1] 。
(2)每一堆都不能为空,所以每一堆先放一个,还剩i-j个苹果这些苹果分为j堆,即dp[i-j][j]。所以状态转移方程为:
dp[i][j] = dp[i-1][j-1]+dp[i-j][j];
思路二: 因为每一堆都不能为空所以我们先将j堆每个放一个苹果,那么还剩i-j个苹果,我们可以将这i-j可苹果任意分为1堆,2堆……i-j堆放在已有的j堆上面,所以方程为:
dp[i][j] = dp[i-j][1]+dp[i-j][2]+dp[i-j][3]+……dp[i-j][j]
注意这里最后分成j份,因为每一堆以及在一开始分了1个,所以之后可以当做分0个在某一堆。我们再代入i = i-1,j = j-1得到:
dp[i-1][j-1] = dp[i-j][1]+dp[i-j][2]+dp[i-j][3]+……dp[i-j][j-1]
两式相减我们可以得到一样的状态转移方程:
dp[i][j] = dp[i-1][j-1]+dp[i-j][j];
代码:
#include<cstdio>
int main(void){
int n,k;
scanf("%d %d",&n,&k);
int dp[210][10] = {0};
for(int i = 1;i <= n;i++){
dp[i][1] = 1;
}
for(int i = 1;i <= n;i++){
for(int j = 2;j <= k && j<=i;j++){ //这里 i必须不小于j
dp
[i][j] = dp[i-1][j-1]+dp[i-j][j];
}
}
printf("%d",dp[n][k]);
return 0;
}
下面是类似的题目不过每个盘子可以为空
找苹果
题目描述
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分发(5,1,1和1,1,5是同一种方法)
输入格式
第一行是测试数据的数目t(0 <= t <= 20),以下每行均包括二个整数M和N,以空格分开。1<=M,N<=10
输出格式
对输入的每组数据M和N,用一行输出相应的K。
输入输出样例
输入
1
7 3
输出
8
解题思路:本题思路和上面类似dp[i][j]表示i个苹果放在j个盘子中的情况。当i < j是及苹果数少于盘子数dp[i][j] = dp[i][i],当i >= j时我们考虑最后一个盘子如果为空则dp[i][j-1],不为空将每个盘子都放一个苹果还剩i-j个苹果放在j个盘子中dp[i-j][j]。这里需要注意初始化数组dp,当任意数量苹果只能放一个盘子时,只有一种情况,当没有苹果时不管盘子数量如何都为1。
代码:
#include<cstdio>
int main(void) {
int t;
int dp[15][15] = { 0 };
scanf("%d", &t);
while (t--) {
int M, N;
scanf("%d %d", &M, &N);
for (int i = 1; i <= M; i++) {
dp[i][1] = 1;
dp[0][i] = 1;
}
for (int i = 1; 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 - 1] + dp[i - j][j];
}
}
printf("%d\n", dp[M][N]);
}
return 0;
}
下面是递归的写法,要容易理解一点。
递归分为三种情况:
- 苹果数为0,或者盘子数为1都只有1中情况。
- 苹果数少于盘子数那么目前苹果的排列情况与把空盘子减去的排列情况相同,因为空盘子没有用上。
cul(M,N) = cul(M,M)
- 苹果数大于等于盘子数分为两种情况,有空盘子那么cul(M,N) = cul(M,N-1)即先把一个空盘子减去。没有空盘子,那么把每个盘子中的苹果数量都先减去1,cul(M,N) = cul(M-N,N)。综上
cul(M,N) = cul(M,N-1)+cul(M-N,N)
#include<cstdio>
int cul(int M,int N){
if(M == 0|| N == 1)
return 1;
else if(M < N)
return cul(M,M);
else
return cul(M-N,N) + cul(M,N-1);//有空盘子 无空盘子
}
int main(void){
int M,N;
scanf("%d %d",&M,&N) ;
printf("%d",cul(M,N));
return 0;
}