您的位置:首页 > 其它

整数划分问题

2012-03-15 23:12 204 查看
整数划分问题是一个经典问题,几乎在讲算法设计的书中都会讲,下面把主要的思想给总结下。

所谓整数划分,就是将一个正整数n划分为一系列的正整数之和,如将n可以划分为:{m1,m2,m3,m4,m5..mk}(1=<k<=n),则{m1,m2,m3,m4,m5..mk}就是n的一个整数划分。下面我们要做的是,要找到所有的n的正整数划分。

我们该如何找出所有的划分呢?

我们可以先来看看整数划分的规律:

譬如正整数:6

划分情况如下:

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

观察第一列的数字发现,我们划分过程中总是按照比6小1,小2,小3,小k,的顺序来依次排列它的划分,第一列找到比6小k的数之后,然后我们再排列剩下的数字。

所以我们只需要找到那些比6小的数字开头的,这一行的排列的数目,然后把这些行加起来就是所有的结果,注意看每一行,划分的特征是,所有的数字都不超过第一列的那个数字,所以我们可以进行如下的尝试。

我们试图这样去思考这个问题:

如果{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被认为是同一个划分。
so,该问题变得清晰了,求出n的所有划分个数,即f(n, n)。
下面我们考虑求f(n,m)的方法;
递归法

先讨论m,n之间的大小关系:

a.当n==1时,m无论为何值,f(n,m)=1

b.当m==1时,n无论为何值,f(n,m)=1

c.当n<m时,由于排列中没有负数,m多出n的数,没有了实际意义,其实本质上 还是等于f(n,n)。

d.当m==n时,可以分两种情况考虑

1.排列中包含n,则f(n,m)=1

2.排列中不包含n,则问题转化为求f(n,m-1)

根据组合数学中加法规则,应将上述两种情况加起来

故此种情况下f(n,m)=1+f(n,m-1);

e.当n>m时,应分两种情况考虑:

1.排列中包含m,则排列为{m,x1,x2,x3,xk},其中{x1,x2,x3,xk}之和为n-m,

我们注意到x1.x2,x3,xk,是不超过m的,所以对于后面这些之和为n-m的元素来 说,它们的排列数目应该为f(n-m,m)

2.排列中不包含m,则问题转化为f(n,m-1)

根据组合数学中的加法原则,应将上述情况加起来:

故此情况下的f(n,m)=f(n-m,m)+f(n,m-1);



综合以上情况,我们可以看出,上面的结论具有递归定义特征,其中a和b属于回归条件,c和d属于特殊情况,将会转换为情况e。而情况e为 通用情况,属于递推的方法,其本质主要是通过减小m以达到回归条件,从而解决问题。其递推表达式如下:

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)



因此我们可以给出求出f(n, m)的递归函数代码如下:

这个是用c++实现的

//整数划分
#include<iostream>
using namespace std;
int Partition(int n,int m){
    if(n==1||m==1)  return 1;
    if(n<m)         return Partition(n,n);
    if(n==m)        return Partition(n,m-1)+1;
    if(n>m)         return Partition(n,m-1)+Partition(n-m,m);
    }
int main(){
    int n,m;
    while(cout<<endl<<"please input two int:"<<endl&&~scanf("%d%d",&n,&m))
        cout<<"The count of partition intger "<<n<<" is: "<<Partition(n,m)<<endl;
        
    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: