整数划分问题(递归&非递归)

递归算法:

将正整数n表示成一系列正整数之和,n=n1+n2+...+nk,其中n1>=n2>=n3>=...>=nk>=1,k>=1。

正整数n的这种表示称为正整数n的划分。正整数n的不同的划分个数城外正整数n的划分数,记作p(n)。

例如,正整数6有如下11种不同的划分,所以p(6)=11。

6;

5+1;

4+2;4+1+1;

3+3;3+2+1;3+1+1+1;

2+2+2;2+2+1+1;2+1+1+1+1;

1+1+1+1+1+1。

求解:

在正整数n的所有不同的划分中,将最大加数n1不大于m的划分个数记作q(n,m)。可以建立q(n,m)的递推关系。

(1)q(n,1)=1,n>=1。

当最大加数n1不大于1时,任何正整数n只有一种划分形式,即n=1+1+1.....+1。

(2)q(n,m)=q(n,n),m>=n。

最大加数n1实际上不能大于n。因此q(1,m)=1。

(3)q(n,n)=1+q(n,n-1)。

正整数n的划分由n1=n的划分和n1<=n-1的划分组成。

(4)q(n,m)=q(n,m-1)+q(n-m,m),n>m>1。

正整数n的最大加数n1不大于m的划分是由n1=m的划分和n1<=m-1的划分组成。


综上所述,设计计算q(n,m)的递归算法如下,其中正整数n的划分数p(n)=q(n,n)。

#include<iostream>
using namespace std;
int q(int n,int m)
{
	if((n<1)||(m<1)) return 0;
	if((n==1)||(m==1)) return 1;
	if(n<m) return q(n,n);
	else if(n==m) return q(n,m-1)+1;
	return q(n,m-1)+q(n-m,m);
}
int main()
{
	cout<<q(6,6)<<endl;
	return 0;
}

整数划分问题(递归&非递归)_第1张图片


非递归算法:

同样一个题目,换一种描述方式。

问题描述:
把 M 个同样的苹果放在N 个同样的盘子里,允许有的盘子空着不放,问共有多少
种不同的分法?(用K 表示)注意:5,1,1 和1,5,1 是同一种分法。
输入数据
第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含两个整数M 和N,以
空格分开。1<=M,N<=10。
输出要求
对输入的每组数据M 和N,用一行输出相应的K。
输入样例
1
7 3
输出样例
8

解题思路:
所有不同的摆放方法可以分为两类:至少有一个盘子空着和所有盘子都不空。我们可以分别计算这两类摆放方法的数目,然后把它们加起来。对于至少空着一个盘子的情况,则N 个盘子摆放M 个苹果的摆放方法数目与N-1 个盘子摆放M 个苹果的摆放方法数目相同。对于所有盘子都不空的情况,则N 个盘子摆放M 个苹果的摆放方法数目等于N 个盘子摆放M-N 个苹果的摆放方法数目。我们可以据此来用递归的方法求解这个问题。
设f(m, n) 为m 个苹果,n 个盘子的放法数目,则先对n 作讨论,如果n>m,必定有n-m 个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响;即if(n>m) f(m,n) =f(m,m)。当n <= m 时,不同的放法可以分成两类:即有至少一个盘子空着或者所有盘子都有苹果,前一种情况相当于f(m , n) = f(m , n-1); 后一种情况可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m , n) = f(m-n , n)。总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n) 。整个递归过程描述如下:

int f(int m , int n){
  if(n == 1 || m == 0) return 1;
  if(n > m) return f (m, m);
    return f (m , n-1)+f (m-n , n);
}

使用动态规划,改成非递归的形式。

#include<iostream>  
using namespace std;  
int dp[101][101];  
int solve (int m,int n)
{  
    for (int i = 1;i <= n;i++)
		dp[0][i] = 1;  
    for (int i = 1;i <= m;i++)
	{  
        dp[i][0] = 0;  
        for (int j = 1;j <= n;j++)  
		{ 
            if(i < j)   
                dp[i][j] = dp[i][i];  
            else  
                dp[i][j] = (dp[i-j][j]+dp[i][j-1]); 
		}
    }  
    return dp[m][n];  
}  
int main() {  
    int n,m,ans;  
    while(scanf("%d%d",&m,&n)!=EOF)  
    {  
        ans = solve(m,n);  
        printf("%d\n",ans);  
    }  
    return 0;  
}  



参考

王晓东《算法设计与分析(第2版)》P23

程序设计导引及在线实践P178

你可能感兴趣的:(整数划分问题(递归&非递归))