首先用递归的思想来思考这道题:
1,递归出口:当只有一个盘子或者 含有 0 个 或 1 个苹果的时候只有一种方法
2,当盘子数 n 大于苹果数 m 时,则必有 n - m 个空盘子,所以只需求 m 个盘子
放 m 个苹果时的方法数即可,
3,当盘子数 n 小于等于 苹果数 m 时,总方法数 = 当含有一个空盘子时的方法数
- 不含空盘子时的方法数。
原因:当在求只含有一个空盘子时的方法数时,已经包含了含有 2 ~ n - 1 个空盘子 的情况。
不含空盘子的计算:先将每个盘子装一个苹果,则问题变成了求 n 个盘子放 m - n
个苹果的方法数了。
其间,还可用记忆搜索优化(此处不再详讲)
然后用动态规划来优化代码:
新建一个动态规划表 dp;dp[i][j] 表示 i 个盘子放 j 个苹果的方法数。
则 当 i > j 时,dp[i][j] = dp[i - (i - j)][j] = dp[j][j](原因上面已经讲过)
当 i <= j 时,dp[i][j] = dp[i - 1][j] + dp[i][j - i];
最后dp[n][m] 就是所求。
#include <iostream> using namespace std; const int N = 21; int dp[N][N]; int main(){ int m, n, k; while(cin >> m >> n){ for(int i = 0; i <= m; i++){ for(int j = 1; j <= n; j++){ if(i == 0 || j == 1){ dp[i][j] = 1; }else 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; }
递归
#include <iostream> using namespace std; const int N = 21; int func(int m, int n){ if(m == 0 || n == 1){ return 1; }else if(m < n){ return func(m, m); }else{ return func(m, n-1) + func(m-n, n); } } int main(){ int m, n, k; while(cin >> m >> n){ int ans = func(m, n); printf("%d\n", ans); } return 0; }