思路:
第一次尝试(错误):将M个苹果转换成为不同重量堆的苹果,使用0-1背包解决,失败:原因是在苹果分堆在聚合过程后无法控制总数恰好等于M;
第二次尝试(错误):注意到M个苹果放到N个盘子里,依次相加空出0个、1个……N个盘子各个数量,失败;原因是调用M个苹果装到N-k个盘子会有重复:例如将(M=4,N=3)时,4个苹果分别放入3个、2个、1个盘子时分别结果为1,3,1,会有重复因为放入2个盘子的(0,4)放法和放入一个盘子相同。
第三次尝试(成功):令f(n,m)表示n个盘子盛放m个苹果,注意到拿n-1个盘子(可以有空盘子)放m个苹果和n个盘子(无空盘子)放m个苹果无重复
所有有 子问题1:f(n,m)[容许有空盘子] = f(n-1,m)[容许有空盘子] + f(n,m)[无空盘子]
而什么时候n个盘子放m个苹果没有空盘子呢,就是n个盘子每个至少放一个:
所以有 子问题2: f(n,m)[无空盘子] = f(n,m-n) [容许有空盘子]
可以得到转移方程:f(n,m)[容许有空盘子] = f(n-1,m)[容许有空盘子] +f(n,m-n) [容许有空盘子];
也就是f(n,m) = f(n-1,m)+f(n,m-n);
注意边界条件 : f(0,0) = 1;
f(0,k) = 0 , k!=0
以下给出递归和动态规划两种解决办法,请诸君参考:
#include <iostream> using namespace std; // 递归求解 // int f(int n,int m) // { // if(n == 0 && m == 0) return 1; // else if(n == 0 && m != 0) return 0; // else if( m >= n) return f(n-1,m)+f(n,m-n); // else return f(m,m); // } // 动态规划求解 int dp[11][11]; void Initial() { for(int i = 0;i<=10;i++) { for(int j = 0;j<=10;j++) { // i=0的初始化可以放到循环外面写,这样在循环时可以减少判断次数 if(i == 0 && j == 0) dp[i][j] = 1; else if(i == 0 && j != 0) dp[i][j] = 0; // 盘子数少的时候,空出m-n个盘子,转化成m个盘子盛放m个苹果 else if(i > j) dp[i][j] = dp[j][j]; else dp[i][j] = dp[i-1][j] + dp[i][j-i]; // 状态转移方程 } } } int main() { int m,n; Initial(); while(cin >> m >> n) { cout << dp[n][m] << endl; } }