Loading

生成函数解决多重集合的计数问题

生成函数解决多重集合的计数问题

组合计数问题

描述

多重集合 $ S= {n_1\cdot{a_1},n_2\cdot{a_2}...n_k\cdot{a_k}}\(其中\)a_1,a_2...a_k\(代表不同的元素, \)n_1,n_2...n_k$分别代表它们的个数;主要问题求S的r集合(无序)的个数

简单生成函数(也称作生成函数)

\(h_0,h_1,...h_n...\)的生成函数为级数\(g(x)=h_0+h_1x+h_2x^2+...h_nx^n+...\);
那么上述组合计数问题可化为:

\(g_1(x)=1+x+x^2+..x^{n_1}\)
\(g_2(x)=1+x+x^2+..x^{n_2}\)
\(....\)
\(g_k(x)=1+x+x^2+..x^{n_k}\)
S的r集合的个数即为\(g_1(x) \cdot g_2(x) \cdot ....\cdot g_k(x)\)\(x^r\)前的系数\(h_r\)
所以问题变成了多项式的乘积(实际只用求到次数r)
朴素算法复杂度\(O(r^2k)\);
当然具体情况可根据条件而定,上述只是最简单的情况;
更为复杂的如求S的子集中满足元素和为\(r\)的个数;

大佬语录:
把组合问题的加法法则和幂级数的乘幂对应起来
把离散数列直接的结合关系对应成幂级数间的运算关系
简单题

简单模板

#include<bits/stdc++.h>
using namespace std;

//生成函数/母函数 generating function
//
int a[3];// 1,2,5 三种硬币数量
int b[5] = {1,2,5};
const int maxn = 8050;
int c[maxn], tc[maxn];


int main(){
    while(cin>>a[0]>>a[1]>>a[2]){
        if(a[0]==0&&a[1]==0&&a[2]==0) break;
        //
        memset(c,0,sizeof(c));
        memset(tc,0,sizeof(tc));
        for(int i=0;i<=1*a[0];i++){
            c[i]=1;// c[i]记录当前 x^i 前面的系数
        }
        for(int i=2;i<=3;i++){//i=2 表示处理第二种硬币 (1+x+x^2..)*(1+x^2+x^4..)
            for(int j=0;j<=b[i-1]*a[i-1];j+=b[i-1]){//幂次b[i-1]的整数次幂
                for(int k=0;k+j<maxn&&k<maxn;k++){
                    tc[k+j] += c[k];//tc[i] 用于暂存 次数为i的系数
                }
            }
            for(int s=0;s<maxn;s++){
                    c[s] = tc[s];
                    tc[s] = 0;
            }

        }
        for(int i=0;i<maxn;i++){
            if(c[i]==0){cout<<i<<endl;break;}
        }
        //over
    }
    return 0;
}

排列计数问题

描述

同上,不过求得的有序排列的数目,此时需要考虑指数型生成函数,具体证明见组合数学.
指数型生成函数:\(g^{(e)}(x) = \sum_{n=0}^{\infty}h_n{\frac{x_n}{n!}}\)
采取上述同样的做法,从编程的角度\(\frac{1}{n!}\) 采用小数处理,复原是需乘上\(n!\);
因为求得\(h_nx^n = h_n\cdot{n!}\cdot{\frac{x^n}{n!}}\)

简单题

简单模板

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 12;
double a[maxn] ,b[maxn],fac[maxn];
int num[maxn];
int n,m;
int main(){
    
    while(cin>>n>>m){
    for(int i=1;i<=n;i++){
        cin>>num[i];
    }
    fac[0]=1;
    for(int i=0;i<=m;i++){
        a[i]=b[i]=0;
    }
    for(int i=1;i<=m;i++){
        fac[i] = fac[i-1]*i;
    }
    for(int i=0;i<=num[1];i++){
        a[i] = 1.0/fac[i];
    }
    for(int i=2;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k<=num[i]&&(j+k)<=m;k++){
                b[j+k]+=a[j]*1.0/fac[k];
            }
        }
        for(int j=0;j<=m;j++){
            a[j] = b[j];
            b[j] = 0;
        }
    }
    double t = a[m]*fac[m];
    printf("%.0f\n",t);
    }
    //cout<<t<<endl;
    //int ans = (int)(a[m]+0.1);
    //cout<<ans<<endl;

}
posted @ 2019-06-24 19:07  fridayfang  阅读(536)  评论(0编辑  收藏  举报