一道有关排列组合的问题(求正整数s分成n个正整数的方法数)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一道有关排列组合的问题(求正整数s分成n个正整数的方法数)相关的知识,希望对你有一定的参考价值。

首先,对于不定方程
x(1)+x(2)+...+x(n)=s (s>=n>=1)
的正整数解的个数为 C(s-1,n-1)=C(s-1,s-n) 种

但是,如果规定x(1)<=x(2)<=...<=x(n)
那么又应该是多少种呢?

显然不是除以2,因为任何符合条件的x(n)的反序也不完全是符合条件的(有x(1)=x(2)=...=x(n)例外),而且任何不符合条件的x(n)的反序也不一定满足 x(1)<=x(2)<=...<=x(n),所以问题变得有点复杂。

现在的问题是:
(1)用s,n的函数表示不定方程
x(1)+x(2)+...+x(n)=s (s>=n>=1,x(1)<=x(2)<=...<=x(n)都为正整数)
的正整数解的个数;若难以用函数表示,请给出一种算的方法(即任意给出s、n,通过该方法能算出答案)

(2)另外,设种类数为k,如果对于第i种结果(1=<i<=k),能表示成
a(i,1)个b(i,1),a(i,2)个b(i,2),...,a(i,?)个b(i,?)的形式(则更好)
【举例:5可拆为
1,1,1,1,1 5个1 a(1,1)=5,b(1,1)=1
1,1,1,2 3个1,1个2,a(2,1)=3,b(2,1)=1,a(2,2)=1,b(2,2)=2
1,1,3 2个1,1个3 a(3,1)=2,b(3,1)=1,a(3,2)=1,b(3,2)=3
1,2,2 1个1,2个2 a(4,1)=1,b(4,1)=1,a(4,2)=2,b(4,2)=2
1,4 1个1,1个4 a(5,1)=1,b(5,1)=1,a(5,2)=1,b(5,2)=4
2,3 1个2,1个3 a(6,1)=1,b(6,1)=2,a(6,2)=1,b(6,2)=3
5 1个5 a(7,1)=1,b(7,1)=5
其中k=7】

以上两问,回答任意问即可给分,回答两问则追加100分
补充:上面的举例写错了,应该是没有什么大影响的

1楼的方法可以算出,思路是从最小数出发,实际上采用的递归的方法,这种方法在编程中可以运用,但不是我想要的,希望能有一个公式。
具体说来,举个例子,n个不同元素中挑k个的方法,你完全可以说

操作f(n,s)表示n个不同元素中挑k个的方法数
先分选第一个,则变为f(n-1,s-1)
若不选第一个,则变为f(n-1,s)
以此类推...这样的话,说了和没说一样
而实际上它是C(n,k)个

我的意思就是它能用一个函数表示,而其中有的参数可以通过试验得到
(表达式类似于"孙子剩余定理"的表示方式)

你的两个问题在数学界早有人探索过了.叫做拆分数问题.是组合数学研究的课题.
你的第一个问题等价于将整数s拆分为n个正整数的拆分数.关于这个问题有几个定理:
1.将整数r拆分为k个正整数的拆分数,等价于将r拆分为最大数为k的拆分数.证明略,你有兴趣可以去搜索下费勒斯图像.
2.将整数r拆分为重复度不超过k的拆分数,等价于将r拆分为不能被k+1整除的拆分数.可以用母函数证明.
所以你的第一个问题可以等价成将s拆分为最大数为n的拆分数,或者将s-n拆分为最大数不超过n的拆分数.
关于该拆分数的编程上的计算方法目前有很多,但是据我所知,在数学上目前还没有公式可以表示.递推公式也没有.
你的第二个问题中的k很容易求,这个k叫做一般拆分数,记做p,例如p(5)=7.
有如下递推关系:
p(n)=p(n-1)+p(n-2)-p(n-5)-p(n-7)+...+(-1)^(m-1)*p(n-1/2*m(3m+或-1)),这个也可以用母函数证明.一句多余的话,母函数是组合数学中的重要工具.
还有关于拆分数的穷举,在数学上也没有有效的办法,还是需要借助计算机.
如果你希望获得关于拆分数穷举的程序或者算法,可以再联系我.本人是学计算机的.
参考技术A 首先很好奇你这个问题的动机,数学探究?信息学竞赛?看来后者的可能性更大一些
回答你的两个问题:
1.x1+x2+...+xn=s的正整数解个数(不计未知数的排列顺序)
问题相当于把S个无差别的东西放到N个无差别的盘子里,问总方法数
闭形式的解我不知道,但如果令SUM[S][N]所求的方法数,可以建立下面的递推式:
SUM[S][N]=SUM[S-1][N-1]+SUM[S-N][N]
边界条件SUM[0][N]=0,SUM[1][1]=SUM[2][1]=...=SUM[N][1]=1
解释一下,SUM[S-1][N-1]代表某个盘子里的东西个数是1的方案数,SUM[S-N][N]代表所有盘子里的东西数都>1的方案数
程序设计方面,用正向动态规划可以求出SUM[S][N],时间复杂度O(SN),空间复杂度最低O(N)(滚动数组)

2.
问题等价于把所有的S-K分解按照下面的规则排序:
1.K值较大的排在前面(例如1,1,1,1,1>1,1,1,2
2.K值相同的字典序在先的排在前面(例如1,1,1>1,2,2)
求第M个排列的形式
我们不妨叫所求排列为M-排列
SUM[a][b]的含义和1的相同
STEP1:
确定值N,使得SUM[S][S]+SUM[S][S-1]+..+SUM[S][N+1]<M≤SUM[S][S]+SUM[S][S-1]+...+SUM[S][N]
显然N就是M-排列的长度
M减去SUM[S][S]+SUM[S][S-1]+...+SUM[S][N+1]
STEP2:
确定数P1,使得SUM[S-1][N-1]+SUM[S-2][N-1]+...+SUM[S-(P1-1)][N-1]<M≤SUM[S-1][N-1]+SUM[S-2][N-1]+...+SUM[S-P1][N-1]
此时P1就是M-排列的第一个元素
M减去SUM[S-1][N-1]+SUM[S-2][N-1]+...+SUM[N-(P1-1)][N-1]
STEP3:
重复STEP2的步骤,可以计算出P2,P3...PN
这样M排列=P1,P2,...PN,1≤PN≤S
这样你的a(i,j),b(i,j)就知道了
程序实现可以用递归形式求解,时间复杂度同样是O(SN)或O(logS*N)(二分查找优化),空间复杂度O(1)。
最后再反驳你一点论点:
C(n,k)=C(n-1,k-1)+C(n-1,k)"说了等于没说"
这个我不敢苟同。虽然你可以把C(n,k)写成n!/k!/(n-k)!,但是递推式是明确的给出一种可操作的计算组合数的方法。能求得数学上的闭形式解当然是很了不起的。但首先能否求得出来是一回事,其次就算求得出来,形式可能复杂到实际计算的代价和递推相比更差!(当然,如果是纯理论研究不在此例)
希望回答对你有所帮助!
参考技术B 设f(s,n)为方程
x(1)+x(2)+...+x(n)=s (s>=n>=1) x(1)<=x(2)<=...<=x(n)的正整数解的个数,m=[(s+n-1)/n](即(s+n-1)/n的整数部分),
当x(1)=1时,方程变为x(2)+...+x(n)=s-1,1<=x(2)<=...<=x(n),于是该方程解的个数为f(s-1,n-1).
当x(1)=2时,令y(k)=x(k)-1,k=2,3,...,n,则方程变为y(2)+...+y(n)=s-n-1,约束条件为2<=x(2)<=...<=x(n),即1<=y(2)<=...<=y(n),该方程解的个数为
f(s-n-1,n-1).
依此类推,当x(1)=k时,方程解的个数为f(s+n-1-nk,n-1),k=1,2,...,m.
故f(s,n)=f(s-1,n-1)+f(s-n-1,n-1)+...+f(s+n-1-mk,n-1).
易知,f(1,1)=f(1,2)=...=f(1,n)=1
对给定的s,n
从f(1,1),f(1,2),..,f(1,n)开始按上面的方法依次算出
f(2,1),f(2,2),..,f(2,n).....一直到f(s,n)
参考技术C C(s,n)就是。
举个例子,12345分成两个数,C(5,2)种分法,(5*4)/(1*2)。
C(s,n)=(s*s-1*s-2*...*s-n+1)/n!。保证分子分母项数都是n。
另,为了简便,C(s,n)=C(s,s-n)这个很容易证明。
参考技术D 哎,算了,上面都写了那么多了。。。

NYOJ 90 —— 求n划分为若干个正整数的划分个数

整数划分

时间限制:3000 ms  |  内存限制:65535 KB
描述
将正整数n表示成一系列正整数之和:n=n1+n2+…+nk, 
其中n1≥n2≥…≥nk≥1,k≥1。 
正整数n的这种表示称为正整数n的划分。求正整数n的不 
同划分个数。 
例如正整数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。 
输入
第一行是测试数据的数目M(1<=M<=10)。以下每行均包含一个整数n(1<=n<=10)。
输出
输出每组测试数据有多少种分法。
样例输入
1
6
样例输出
11

下面转自博客:http://www.cnblogs.com/hoodlum1980/archive/2008/10/11/1308493.html

  所谓整数划分,是指把一个正整数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;                                        ( 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 )

1)自顶向下实现的版本:

#include <cstdio>
#include <iostream>

using namespace std;

int f(int i, int j)
{
    if(i==1 || j==1)    return 1;
    if(i<j)    return f(i, i);
    if(i==j)    return f(i, i-1) + 1;
    return f(i, j-1) + f(i-j, j);    
}

int main ()
{
    int T, n;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        printf("%d\\n", f(n, n));    
    }
        
    return 0;
}

 

2)自底向上实现的版本:

#include <cstdio>
#include <iostream>

using namespace std;

const int MAXN = 15;

int dp[MAXN][MAXN];

void init()
{
    for(int i=1; i<MAXN; i++) {
        for(int j=1; j<MAXN; j++) {
            if(j>i)    dp[i][j] = dp[i][i];
            else if(j == i)    dp[i][j] = dp[i][j-1] + 1;
            else    dp[i][j] = dp[i][j-1] + dp[i-j][j];
        }
    }
}

int main ()
{
    init();
    
    int T, n;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        printf("%d\\n", dp[n][n]);
    }
    return 0;
}

 

以上是关于一道有关排列组合的问题(求正整数s分成n个正整数的方法数)的主要内容,如果未能解决你的问题,请参考以下文章

求java程序:输入n个正整数,分成两组求和,求两列数字组合使这两组的和最接近平均数

定和的n个正整数 求最大积

7.5 深搜-最佳调度问题(枚举排列+回溯+剪枝)

NYOJ 90 —— 求n划分为若干个正整数的划分个数

用java怎样将一个正整数L随机拆分成N个正整数

[HNOI2001] 求正整数