放苹果
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 27747 | Accepted: 17550 |
Description
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。
Input
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数M和N,以空格分开。1<=M,N<=10。
Output
对输入的每组数据M和N,用一行输出相应的K。
Sample Input
1 7 3
Sample Output
8
这道题目除了用dp做以外,还可以用很多其他的方法过。因为题目数据非常小,各路大牛的博客上有层出不穷的解法,我见过的有可以用DFS递归的方法,组合数学的方法,还有暴力打表法……今天来探讨一下使用dp做的方法。
拿到题目一看,很容易想到用dp[i][j] 表示共i个苹果放入j个盘子的方法。
状态转移的过程一看好像很多。不知如何构建,但是我们可以从目标状态入手!把i个苹果放入j个盘子里的情况无非有两种
1、这j个盘子中有空盘子,这时候i个苹果放入j个盘子的方法和i个苹果放入j-1个盘子的方法是一样的,
得到dp[i][j] = dp[i][j-1] (当满足i<j时,此情况必定成立。)
2、 j个盘子中没有空盘子,那么我们可以从每个盘子中移除一个,那么原问题转换成把i-j个苹果放到j个盘子里。
题目里面很难理解的一点是,为什么没有空盘子时,苹果只拿走一个?
这个问题我们可以这样想,假设放置最少的盘子中有k个苹果,那么这个状态可以唯一地,由连续k次每次从每个盘子中拿走一个苹果而得到,所以就不会出现重复的子状态,也就满足了题目的显示条件!
想清楚以后,还有边界条件需要非常注意,dp[0][i] = 1,dp[1][i] = 1,dp[i][1] = 1;
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 15;
int dp[MAXN][MAXN];
int M,N;
// 定义dp[i][j]为把i个苹果放在j个盘子里的放法
// dp[i][j] = dp[i][j-1] 或者
// dp[i][j] = dp[i][j-1] +dp[i-j][j]
//
// 如果j个盘子中有空盘子,那么就转换成dp[i][j-1]
// 如果没有空盘子,我们就先给这j个盘子放每个盘子放一个苹果
// 转换成dp[i-j][j]
//
int main()
{
int T;
cin>>T;
while(cin>>M>>N)
{
memset(dp,-1,sizeof(dp));
for(int i = 1; i<=10; i++)
dp[0][i] = dp[1][i] = dp[i][1] = 1;
for(int i = 1; i <= 10; i++)
{
for (int j =1; j<=10; j++)
{
if(dp[i][j] == -1)
{
if (i<j) //证明有空盘子
dp[i][j] = dp[i][j-1];
else //证明没有空盘子,那就先给这j个盘子里面每个
//都放一个苹果,转换成dp[i-j][j];
dp[i][j] = dp[i][j-1] +dp[i-j][j];
}
}
}
cout<<dp[M][N]<<endl;
}
}