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

来源:互联网 发布:html5 自适应表单源码 编辑:程序博客网 时间:2024/05/22 15:49

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

16

Sample Output

111

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)进行存储提高了效率。。。

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

#includeusing 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)

原创粉丝点击