首先要感谢组合数学老师啊~不然还一直坑死在这题呢(一直想用组合数求解)
题目大意是:把n个相同的苹果放入k个相同的盒子里,可以有空盒子,问有多少种方法数?
首先分析一下这个题目的要求:
将n各相同的苹果放入k个相同的盒子里面,问有多少种方法?
最简单的理解就是使用斯特林数(组合数学来求解), 但是由于斯特林数求解方法没有显式公式,所以比较难写,
具体的讨论可见http://bbs.csdn.net/topics/360143008
那么既然放弃了组合数学的求法(一开始一直在想), 就只能使用DP的思想来求解了,假设dp[n][k] 表示将n个球放到k个盒子里的方法数
那么就其有无空盒子来讨论
(1)有空盒子(那么至少一个空盒),这样就要求解出来dp[n][k-1],dp[n][k-2],dp[n][k-3]....dp[n][1];
那么这样就有很多情况了,如果这样求解的话应该会超时( 我也没有试过),所以我们只考虑递推的过程,即假设只 有一个空盒子,
这样就可以求出有两个空盒,3个空盒的情况,递推嘛~
(2)没有空盒(就相当于每个盒子至少放一个球),于是就相当于求dp[n-k][k],相当于已经知道每个盒子都有一个 球了,再将剩下的n-k个球放入k个盒子中
所以动态转方程:dp[n][k] = dp[n][k-1] + dp[n-k][k];
需要注意的就是当n<k的时候,dp[n][k] = dp[n][n];
因为盒子是相同的,空那几个盒子是没有区别的。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int dp[maxn][maxn];
void dpsum(int n,int k)
{
if(n==0 || k==1) //递推的起点
{
dp[n][k] = 1;
return ;
}
if(n<k)
{
if(dp[n][n]==-1) //dp[n][n]没有求算过
{
dpsum(n,n);
}
dp[n][k] = dp[n][n];
return ;
}
//表示dp[n][k-1]存在(没有越界),且没有求算过
if(k>=1 && dp[n][k-1]==-1)
{//求算dp[n][k-1]
dpsum(n,k-1);
}
//表示dp[n-k][k]存在(没有越界),且没有求算过
if(n>=k && dp[n-k][k]==-1)
{//求算dp[n-k][k]
dpsum(n-k,k);
}
//动态转移方程
dp[n][k] = dp[n-k][k] + dp[n][k-1];
return ;
}
int main()
{
int n,k;
memset(dp,-1,sizeof(dp));
//因为求解的都是固定的值,所以就只需求解一次就行了
while(~scanf("%d%d",&n,&k))
{
if(dp[n][k] == -1) //没有求算过
{
dpsum(n,k);
}
printf("%d\n",dp[n][k]);
}
return 0;
}