母函数与指数型母函数小结

母函数:所谓的母函数我觉得就是将一些组合数问题转化成多个多项式相乘,然后对系数做一些处理....

指数型母函数:这里讲的非常不错

泰勒公式:

e^x = 1 + x/1! + x^2/2! - x^3/3! + ... + x^n/n!;

e^-x = 1 - x/1! +x^2/2! - x^3/3! + ... +(-) x^n/n!;

关于母函数的题有HDU 1171,1398,1709,2065,2069,2082,2152;POJ 3046,3716,3734

1.hdu 1171

题目大意给出一个n,接下来n行每行有v, m两个数表示有m个值为n的数,将这两个数分成两堆让两堆的差值的绝对值最小,其实用背包就可以轻易的解决这个问题,我们用母函数的思想来做一下。

首先对每一组v,m 我们可以构造出形如这样的公式(x^v+x^2v+x^3v....x^mv)。这个公式的含义就是在m个v中选出一种,然后将所有公式相乘,枚举x幂数最接近sum/2的那一项。

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int maxa = 260000;
int v[100], m[100];
bool a[2][maxa];
int main(){
    int n;
    while(scanf("%d", &n)&&n >=0){
        int sum = 0;
        for(int i = 0;i < n; i++){
            scanf("%d%d", &v[i], &m[i]);
            sum += v[i] * m[i];
        }
        memset(a, 0, sizeof(a));
        a[0][0] = 1;
        for(int i =0 ;i < n; i++){
            for(int k = sum/2; k >= 0; k--){
                if(a[i%2][k]){
                    for(int j = 0; j <= m[i]; j++){
                        if(k+j*v[i] > sum/2)break;
                        a[(i+1)%2][k+j*v[i]] = 1;
                    }
                }
            }
        }
        for(int i = sum/2; i >= 0; i--){
            if(a[n%2][i]){
                printf("%d %d\n", sum-i, i);
                break;
            }
        }
    }
}
View Code

 

2.hdu 1398

题目大意就是用一些x^2的数相加能得到n,有多少种情况,思路同上。

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
const int maxa = 305;
int a[maxa];
int main(){
    int n;
    while(scanf("%d", &n), n){
        for(int i = 0; i <= n; i++){
            a[i] = 1;
        }
        for(int i = 2; i*i <= n; i++){
            for(int k = n; k >= 0; k--){
                for(int j = 1; k + j*i*i <= n; j++){
                    a[k+j*i*i]  += a[k];
                }
            }
        }
        printf("%d\n", a[n]);
    }
}
View Code

 

3.hdu 1709

给出一些和一个天平砝码,问这些砝码在[1,sum]之间不能称出的重量。

做到这的时候就感觉母函数难道就是背包吗...希望下面的题能给我惊喜....

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxa = 20005;
bool w[maxa];
int a[105];
int main(){
    int n;
    while(scanf("%d", &n) !=EOF){
        int sum = 0;
        for(int i =0 ;i < n; i++){
            scanf("%d", &a[i]);
            sum += a[i];
        }
        memset(w, 0, sizeof(w));
        w[10000] = 1;
        int zero = 10000;
        for(int i = 0; i< n; i++){
            for(int k = zero + sum; k >= zero - sum; k --){
                if(w[k]){
                    if(k+a[i] <= sum+zero)
                        w[k+a[i]] = 1;
                }
            }
            for(int k = zero - sum; k <= zero +sum ; k++){
                if(w[k]){
                    if(k - a[i] >= zero -sum)
                        w[k-a[i]] = 1;
                }
            }
        }
        int ans = 0;
        for(int i =zero+1; i <= zero+sum; i++){
            if(w[i] == 0) ans ++;
        }
        if(!ans)printf("0\n");
        else{
            printf("%d\n", ans);
            for(int i =zero+1 ;i <= zero+sum; i++){
                if(!w[i]){
                    ans --;
                    printf("%d", i-zero);
                    if(ans !=0)printf(" ");
                }
            }
            puts("");
        }
    }
}
View Code

 

4.hdu 2065

题目大意:(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数%100

我们可以将这道题写成(1+x/1!+x^2/2!+x^3/3!……)^2*(1+x^2/2!+x^4/4!+x^6/6!……)^2.这样的形式,题目就是要求出x^n/n!的系数

由泰勒公式(1+x/1!+x^2/2!+x^3/3!……)^2*(1+x^2/2!+x^4/4!+x^6/6!……)^2 = e^2x*1/4*(e^x+e^-x)^2=1/4(e^4x+1+2e^2x),再将最后的公式做泰勒展开得到x^n/n!的系数为4^(n-1)+2^(n-1)

#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int quick(int x, long long n){
    if(n == 0)return 1;
    int a = quick(x, n/2);
    a *= a;
    if(n & 1){
        a *= x;
    }
    a %= 100;
    return a;
}
int main(){
    int n;
    while(scanf("%d", &n), n){
        long long x;
        for(int cas = 1; cas <= n; cas ++){
            cin>>x;
            printf("Case %d: %d\n",cas, (quick(4, x-1) + quick(2, x-1))%100);
        }puts("");
    }
}
View Code

 

5.hdu 2069

题目大意,有五种硬币组,给出一个数n问有多少种硬币数不超过一百的组成情况

很水的一道题但是自己蠢被卡了半天....

#include<stdio.h>
#include<string.h>
using namespace std;
int a[5] = {1, 5, 10, 25, 50};
const int maxa = 300;
int dp[maxa][maxa];
int main(){
    int n;
    while(scanf("%d", &n)!=EOF){
        memset(dp, 0, sizeof(dp));
        for(int i = 0;i  <= n; i++){
            dp[i][i] = 1;
        }
        for(int i = 1; i <= 4; i++){
            for(int k = n; k>= 0; k--){
                for(int j = 1; k+j*a[i] <= n; j++){
                    for(int nn = 0; nn <=100; nn++)
                        dp[k+j*a[i]][nn+j] += dp[k][nn];
                }
            }
        }
        int ans = 0;
        for(int i = 0;i <= 100; i++){
            ans += dp[n][i];
        }
        printf("%d\n", ans);
    }
}
View Code

6.hdu 2082

又是个水题...连题意都懒得说...

#include<iostream>
#include<string.h>
#include<stdio.h>
const int maxa = 55;
int dp[maxa];
int a[maxa];
int main(){
    int t;
    scanf("%d", &t);
    while(t--){
        for(int i =0 ;i < 26; i++){
            scanf("%d", &a[i]);
        }
        memset(dp, 0, sizeof(dp));
        dp[0] =1 ;
        for(int i = 1; i <= 26; i++){
            for(int k = 50; k >= 0; k--){
                for(int j = k+i, h = 0; j <= 50 && h < a[i-1]; j+= i, h++){
                    //if(k == 0)printf("%d ", j);
                    dp[j] += dp[k];
                }
            }
        }
        int ans = 0;
        for(int i = 1;i  <= 50; i++){
            ans += dp[i];
        }
        printf("%d\n", ans);
    }
}
View Code

 

posted @ 2015-06-15 16:19  icodefive  阅读(1273)  评论(0编辑  收藏  举报