整数划分

来源:互联网 发布:淘宝代运营sina 编辑:程序博客网 时间:2024/05/17 04:08

参考资料:整数划分问题

一、问题描述:

一个整数总可以拆分为2的幂的和,例如:

7=1+2+4

7=1+2+2+2

7=1+1+1+4

7=1+1+1+2+2

7=1+1+1+1+1+2

7=1+1+1+1+1+1+1

总共有六种不同的拆分方式。再比如:4可以拆分成:4 = 4,4 = 2 + 2,4=1+1+2,4 = 1 + 1 + 1 + 1。用g(n)表示n的不同的幂2拆分的种数,例如g(7)=6。求g(n),1<=n<=1000000,输出g(n)%1000000000。

二、问题分析:

该问题实质上是整数划分问题。整数划分问题是算法中的一个经典命题之一,有关这个问题的讲述在讲解到递归时基本都将涉及。经典的整数划分,是指把一个正整数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)。求f(n,m)的方法一般有两种:递归法,母函数法。

1. 递归法:

f(n, m)=1, n=1, or m=1;

          =f(n, n), n<m;

          =1+f(n, m-1), n=m;

          =f(n-m, m)+f(n, m-1), n>m;

2. 母函数法:

所谓母函数,即为关于x的一个多项式G(x):有 G(x)=a0 + a1*x + a2*x^2 + a3*x^3 + ...,则我们称G(x)为序列(a0, a1, a2, ...)的母函数。对于整数划分,假设n的某个划分中,1的出现个数记为a1,2的个数记为a2,..., i的个数记为ai。显然:ai<=n/i; (0<= i <=n)。因此n的划分数f(n,n),也就是从1到n这n个数字中抽取这样的组合,每个数字理论上可以无限重复出现,即个数随意,使他们的总和为n。显然,数字i可以有如下可能,出现0次(即不出现),1次,2次,..., k次,等等。把数字i用(x^i)表示,出现k次的数字i用 x^(i*k)表示, 不出现用1表示。例如数字2用x^2表示,2个2用x^4表示,3个2用x^6表示,k个2用x^2k表示。则对于从1到N的所有可能组合结果我们可以表示为:

G(x) = (1+x+x^2+x^3+...+x^n) (1+x^2+x^4+...) (1+x^3+x^6+...) ... (1+x^n)

        = g(x,1) g(x,2) g(x,3) ... g(x, n)

        = a0 + a1* x + a2* x^2 + ... + an* x^n + ... ;  (展开式)

上面的表达式中,每一个括号内的多项式,即g(x,i) 代表了数字i的参与到整数n划分中的所有可能情况。因此该多项式展开后,由于x^a * x^b=x^(a+b),因此 展开式中ai*x^i 就代表了i的划分,展开后(x^i)项的系数ai也就是i的所有划分的个数,即f(n,n)=an。

对整数的幂2划分,设 _m=2^_exp2,2^_exp2<=n<2^(_exp2+1),则有,g(n)=f(n, _m):

1. 递归法:

f(n, m, exp2)=1, n=1, or m=1;

          =f(n, _m, _exp2), n<m;

          =(1+f(n, m/2, exp2-1))%1000000000, n=m;

          =(f(n-m, m)+f(n, m/2, exp2-1))%1000000000, n>m;

2. 母函数法:

G(x, n) = (1+x+x^2+x^3+...+x^n) (1+x^2+x^4+...) (1+x^4+x^8+...) ... (1+x^_m)

        = g(x,1) g(x,2) g(x,4) ... g(x, _m)

        = a0 + a1* x + a2* x^2 + ... + an* x^n + ... ;  (展开式)

三、代码实现:

#include<cstdio>#include<cstring>#include<cassert>unsigned int arr[21][1000001];unsigned int arr2[21][1000001];int getPartitionNum(int n);int getPNum(int n);int main(int argc,char** argv){int n=1,k=0;memset(arr2,0,sizeof(arr2));while(scanf("%d",&n)!=EOF){assert(getPartitionNum(n)==getPNum(n));printf("%d\n",getPNum(n));}return 0;}void getM(int n,int &m,int &exp2){m=1,exp2=0;while((m<<=1)<=n){++exp2;}m>>=1;}int getPartition(int n,int m,int exp2){if(arr2[exp2][n]!=0){return arr2[exp2][n];}else{if(n==1||m==1){return arr2[exp2][n]=1;}if(n<m){getM(n,m,exp2);return arr2[exp2][n]=getPartition(n,m,exp2);}else if(n==m){return arr2[exp2][n]=(1+getPartition(n,m>>1,exp2-1))%1000000000;}else{return arr2[exp2][n]=(getPartition(n-m,m,exp2)+getPartition(n,m>>1,exp2-1))%1000000000;}}}int getPartitionNum(int n){//记忆递归法int m,exp2;if(n<1||n>1000000){return -1;}getM(n,m,exp2);return getPartition(n,m,exp2);}int getPNum(int n){//母函数法int m,exp2;if(n<1||n>1000000){return -1;}memset(arr,0,sizeof(arr));getM(n,m,exp2);for(int i=0,t=1,k;i<=exp2;++i,t*=2){arr[i][k=0]=1;while((k+=t)<=n){arr[i][k]=1;}}for(int i=1,t=2,k,m;i<=exp2;++i,t*=2){m=0,k=t;while(m+k<=n){m=0;while(m<=n){arr[0][m+k]=(arr[0][m+k]+arr[0][m])%1000000000;++m;}k+=t;}}return arr[0][n];}

四、测试结果:

输入:0输出:-1输入:1输出:1输入:2输出:2输入:3输出:2输入:4输出:4输入:7输出:6输入:900输出:990388828输入:10000输出:573449444输入:100000输出:31071078输入:1000000输出:59487556输入:1000001输出:-1




0 0
原创粉丝点击