修改自①http://www.cnblogs.com/dolphin0520/archive/2011/04/04/2005098.html②http://blog.csdn.net/athenaer/article/details/8265234
问题简介
整数划分是指把一个正整数n写成如下形式:
n=m1+m2+...+mi; (其中mi为正整数,并且1 <= mi <= n),则{m1,m2,...,mi}为n的一个划分。
如果{m1,m2,...,mi}中的最大值不超过m,即max(m1,m2,...,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m)。例如当n=4时,他有5个划分,{4},{3,1},{2,2},{2,1,1},{1,1,1,1};(注意4=1+3 和 4=3+1被认为是同一个划分。)该问题是求出n的所有划分个数,即f(n, n)。
问题分析:
(一)递归法:
看到例子很容易想到递归,需要考虑到这几种情况:
1、临界条件:(a)n=1,只有一种划分{1};
(b)m=1,只有一种划分{1,1,1…,1};
2、①当n=m时,
由n=4的栗子可以看出n的划分为,{n}和n-1的所有划分。
即,当划分包含n时,只有{n}一种;
当划分不包含n时,是n的所有n-1划分;
所以,f(n,n)=1+f(n,n-1)。
② 当n>m时,
也分包含m和不包含m两种情况
- 划分中包含m的情况,即{m, {x1,x2,...xi}}, 其中{x1,x2,... xi} 的和为n-m,f(n-m,m),划分数不大于m,再将m加入划分数,就是一定包含m的情况,举个栗子证明一下
n=5,m=3时,即{3,{1,1}}和{3,{2}}即同f(2,3)一样两种情况;n=5,m=2时,{2,{1,1,1}}即同f(3,2)一样一种情况。
- 划分中不包含m的情况,同上面不包含n的情况道理相同,即f(n,m-1)。
所以当n>m时,f(n,m)=f(n-m,m)+f(n,m-1)。
③当n<m时(因为上述情况中的f(n-m),m)可能出现m>n-m的情况所以应考虑此种情况),
因为划分中的mi为正整数,所以不可能出现大于n的数,不然和就大于n了,所以此时为f(n,n)。
综上所述,
1 (n=1 or m=1)
f(n,n)=1+f(n,n-1) (n==m)
f(n,m)= f(n-m,m)+f(n,m-1) (n>m)
f(n,n) (n<m)
参考代码
using namespace std; int equationCount(int n,int m) { if(n==1||m==1) return 1; else if(n<m) return equationCount(n,n); else if(n==m) return 1+equationCount(n,n-1); else return equationCount(n,m-1)+equationCount(n-m,m); } int main(void) { int n; while(scanf("%d",&n)!=EOF&&(n>=1&&n<=120)) { printf("%d ",equationCount(n,n)); } return 0; }
(二)动态规划
这个题还可以用DP,这个题的每个阶段的最优状态可以从之前某个阶段的某个状态得到而不管之前这个状态是如何得到的。举个栗子,
当n=4,m=4时,4的划分有,4,3+1,2+2,而,3+1由1+3的划分组成,2+2由2的划分+2的划分组成。公式和各种情况在递归的方法中已经推出。
还要注意的是需要先初始化dp[i][0]为0,即当m=0时,显然n没有划分为0。
参考代码
#include <iostream> #include <cstdio> #define MAX 51 using namespace std; int main() { int dp[MAX][MAX]; int n,m; while(scanf("%d",&n)!=EOF){ m=n; for(int i=0;i<=n;i++){ dp[i][0]=0; } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(i==1||j==1){ dp[i][j]=1; } else if (i<j){ dp[i][j]=dp[i][i]; } else if(i==j){ dp[i][j]=1+dp[i][j-1]; } else if(i>j){ dp[i][j]=dp[i-j][j]+dp[i][j-1]; } } } printf("%d ",dp[n][m]); } return 0; }