正整数划分问题(递归优化)

Description

将一个正整数n表示成一系列正整数的和,如:
N=n1+n2+…+nk (其中n1≥n2≥…≥nk≥1, k≥1)
正整数n的一个这种表示成为正整数n的一个划分。
现在给出一个正整数n(80≥n≥1),求n的不同划分一共有多少种。

Input

输入数据包含多组测试数据,每组测试数据只有一行,仅包含一个正整数n。

Output

对每组输入数据,输出一行,输出n的不同划分的种类数。

Sample Input

1
6

Sample Output

1
11

links:http://jsj.sdibt.edu.cn/JudgeOnline/showproblem?problem_id=1497

 

算法课本上的对正整数划分的讲解如下:

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

① q(n,m) = 1, n >= 1    当最大加数n1不大于1时,任何正整数n只有一种划分形式,即 n = 1+1+1+~~~+1(n个1)

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

③ q(n,n) = 1 + q(n,n-1)  正整数n的划分由n1 = n的划分和n1<=n-1 的划分组成

④ q(n,m) = q(n,m-1) + q(n - m,m)   n>m>1 正整数n的最大加数n1不大于m的划分由n1 = m的划分和n1 < = m -1 的划分组成

PS:解释一下④  n1 = m时有: n1 + n -m = n 则此时的划分数目就是对n-m的划分,该划分中最大加数不大于m 即:q(n - m,m)

由以上关系可得

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

众所周知,递归的效率并不是很高,当测试数据量很大的时候无法AC于是乎必须进行优化

int InDiv(int n,int m)
{
    int j = m,re = 0;
    while(j >= 1)
    {
        if(n == j)
        {
            if(ss[n][j] == 0) ss[n][j] == 1;
            re+=ss[n][j]; j--; continue;
        }
        if(n < j) {j = n; continue;}
        if(n == 1 || m == 1)
        {
            if(ss[n][j] == 0) ss[n][j] == 1;
            re+=ss[n][j]; return re;
        }
        if(ss[n][j] == 0) InDiv(n - j,j);
        re += ss[n][j];
        j--;
    }
    return re;
}
采用循环替代了部分递归,貌似没有优化多少,依旧TL。。。
 
再次改进:
int ss[81][81];
int IntegerDivide(int n,int m)
{
	if(n == 1 || m == 1) 
	{
		if(ss[n][m] == 0) ss[n][m] = 1;
		return ss[n][m];
	}
	if(n == m) 
	{
		if(ss[n][m] == 0) ss[n][m] = 1+IntegerDivide(n,n-1);
		return ss[n][m];
	}
	if(n < m) 
	{
		if(ss[n][m] == 0) ss[n][m] = IntegerDivide(n,n);
		return ss[n][m];
	}
	if(n < 1 || m < 1) 
	{
		return 0;
	}
	if(ss[n][m] == 0) ss[n][m] = IntegerDivide(n,m-1) + IntegerDivide(n-m,m);
	return ss[n][m];
}

对于已经计算过的q(n,m)进行存储提高了效率。。。

在网上看到别人写的代码附上挺牛的

#include
using namespace std;
int i,j,sum[88],s[100][100];
int main(){
s[0][0]=1;
for(i=1;i<=80;i++)
for(j=1;j<=i;j++)
{
s[i][j]=s[i-1][j-1]+s[i-j][j];
sum[i]+=s[i][j];
};
while(cin>>i)
{
cout<


 

直接从1开始一个递推的计算出1-80所有数的证书划分存入sum。。。时间复杂度O(n^2)

你可能感兴趣的:(优化,算法,测试,input,n2,output)