zoukankan      html  css  js  c++  java
  • 生成函数解决多重集合的计数问题

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

    组合计数问题

    描述

    多重集合 $ S= {n_1cdot{a_1},n_2cdot{a_2}...n_kcdot{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_ncdot{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
    ",t);
        }
        //cout<<t<<endl;
        //int ans = (int)(a[m]+0.1);
        //cout<<ans<<endl;
    
    }
    
  • 相关阅读:
    算法 在一个递增的二维数组中查找一个数
    java web----网络编程基础
    java----集合(Map)
    java----集合(List、set)
    java----数组
    java面试----1
    java----NIO
    java----commons-io
    java----文件操作
    爬虫----爬取答案
  • 原文地址:https://www.cnblogs.com/fridayfang/p/11078870.html
Copyright © 2011-2022 走看看