-
描述
-
把一个正整数m分成n个正整数的和,有多少种分法?
例:把5分成3个正正数的和,有两种分法:
1 1 3
1 2 2
-
输入
-
第一行是一个整数T表示共有T组测试数据(T<=50)
每组测试数据都是两个正整数m,n,其中(1<=n<=m<=100),分别表示要拆分的正数和拆分的正整数的个数。
输出
- 输出拆分的方法的数目。 样例输入
-
2 5 2 5 3
样例输出
-
2 2
n的m划分,记为dp[n][m],可以分为两种类型:
1.含有1的: dp[n][m][1]
2.不含有1的: dp[n][m][0]
① 含有1的划分,可以看做{a1=1,a2,a3,...,am} (a1+a2+a3+...+am=n),那么是不是只要n减去1,就变成了n-1的m-1划分:{a2,a3,...,am}。即,dp[n][m][1]=dp[n-1][m-1]。含有1的n的m划分一一对应于n-1的m-1划分
② 不含有1的划分,可以看做{a1,a2,a3,...,am} (a1+a2+a3+...+am=n && min(ai)>1),我们会发现如果全部ai均减去1,就变成了n-m的m划分:{a1-1,a2-1,a3-1,...,am-1}。即,dp[n][m][0]=dp[n-m][m]。不含有1的n的m划分一一对应于n-m的m划分
根据“加法原理”:
dp[n][m] = dp[n-1][m-1] + dp[n-m][m] (1<m<n)
dp[n][m] = 1 (m=1 或 n)
这里,你会不会觉得我把两个情况简单相加有点草率?会不会因为dp[n][m]只有①和②其中一种情况,而我却考虑了两种而导致算错?
答案是不会!
1)假设n的m划分都含有1,这时按道理说dp[n][m] = dp[n-1][m-1]就好了,而我们加多了一个dp[n-m][m]。但是,我们会发现如果n的m划分都含有1,那么必定有:n-m<m,即dp[n-m][m]是为0的,故不会有问题!
2)假设n的m划分都不含有1,除非n=m,否则不可能有这样的n和m(我们的条件是:n>m)
故,当n>m时,dp[n-1][m-1] 一定要有,dp[n-m][m]是用来修正的,对于1)的情况不会有影响,但是对于不满足1)的情况有正面作用
自底向上实现的版本:
#include <cstdio> #include <iostream> using namespace std; const int MAXN = 105; int dp[MAXN][MAXN]; void init() { for(int i=1; i<MAXN; i++) { for(int j=1; j<MAXN; j++) { if(j>i) dp[i][j] = 0; else if(j == i) dp[i][j] = 1; else dp[i][j] = dp[i-1][j-1] + dp[i-j][j]; } } } int main () { init(); int T, m, n; scanf("%d", &T); while(T--) { scanf("%d%d", &m, &n); printf("%d\n", dp[m][n]); } return 0; }
POJ题目:放苹果
这里与上面的不同就是,poj允许有空的盘子,而NYOJ不允许有。其实,就是变成了将数字m划分为1,2, ... , n个正整数的划分个数之和
#include <cstdio> #include <iostream> using namespace std; int dp[15][15]; int main () { int T, m, n; scanf("%d", &T); while(T--) { scanf("%d%d", &m, &n); for(int i=1; i<=m; i++) { dp[i][1] = 1; for(int j=2; j<=n; j++) { if(j>i) { dp[i][j] = 0; } else if(j == i) { dp[i][j] = 1; } else dp[i][j] = dp[i-1][j-1] + dp[i-j][j]; } } int ans=0; for(int i=1; i<=n; i++) ans += dp[m][i]; printf("%d\n", ans); } return 0; }