目录
放苹果
要求:
总时间限制: 1000ms
内存限制: 65536kB
描述:
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
输入:
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
输出:
对输入的每组数据M和N,用一行输出相应的K。
样例输入:
1 7 3
样例输出:
8
思路分析:
递归:
这个题目难度不是很大,本质是求第二类Stirling数.由于题目中的M与N都较小,不一定非要使用动态规划,用递归也可以很好理解.我们在这里先给出递归的解法.
因为允许有的盘子空着不放,所以用M个苹果放入N个盘子,第一种可能是有盘子不放,第二种可能就是每个盘子都放.后者显然可以化归为M-N个苹果放入N个盘子的问题,而前者并不确定有几个盘子为空,如何处理?
对于有盘子不放的问题,虽然我们并不确定有几个盘子为空,但我们可以先讨论一个盘子为空,即将M个苹果放入N-1个盘子的问题.由于递归的性质,每一次递归都会减1,那么也就将盘子为空的所有情况都讨论到了.
所以递归的代码如下:
#include<iostream>
using namespace std;
static int K=0;
void Place(int M,int N){
/* corner condition */
if(N==1){
K++; return;
}
Place(M,N-1);
if(M>=N) Place(M-N,N);
}
int main(){
int t;
scanf("%d",&t);
for(int i=0;i<t;i++){
K=0;
int M,N;
scanf("%d%d",&M,&N);
Place(M,N);
printf("%d\n",K);
}
}
动态规划:
其实将递归编写出来后,动态规划的方法也就差不多了。我们用一个dp[M][N]来存储M个苹果放入N个盘子的情况.那么根据上面的讨论就有dp[i][j]=dp[i][j-1]+dp[i-j][j].最后初始化一下dp[0]的这一行,我们就可以在循环输入之前将所有结果都算出来,每次输入后直接从dp数组中寻找对应结果即可.
最终代码:
#include<iostream>
using namespace std;
static int dp[11][11];
int main(){
for(int i=1;i<11;i++){
dp[0][i]=1;
}
for(int i=1;i<11;i++){
for(int j=1;j<11;j++){
dp[i][j]=dp[i][j-1]+(i>=j?dp[i-j][j]:0);//Judge whether i>=j
}
}
int t;
scanf("%d",&t);
int M,N;
for(int i=0;i<t;i++){
K=0;
scanf("%d%d",&M,&N);
printf("%d\n",dp[M][N]);
}
}
补充:
其实还可以进一步优化空间,由于此题中dp数组本质是一个下三角矩阵,所以可以不用存储二维矩阵,一维数组来保存下三角的元素即可,但在此题中意义不大并且会使代码可读性变差,所以没有作此优化.