组合数学之 母函数

母函数当中经典的问题  正是整数拆分问题

所谓整数拆分即把整数分解成若干整数的和(相当于把n个无区别的球放到n个无标志的盒子,盒子允许空,也允许放多于一个球)。
整数拆分成若干整数的和,办法不一,不同拆分法的总数叫做拆分数。


hdu 1028  裸的整数拆分问题

直接来个模版

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100
#define INF 0x7fffffff
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int a[130],b[130];

//g(x)=(1+x+x^2+x^3+……)(1+x^2+x^4+x^6+……)(1+x^3+x^6+……).....


int main()
{
//freopen("ceshi.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=0;i<=n;i++)
        {
            a[i]=1;
            b[i]=0;//首先对a初始化,由第一个表达式(1+x+x2+..xn)初始化,把质量从0到n的所有值都初始化为1.
        }
        for(int i=2;i<=n;i++)// i从2到n遍历,这里i就是指第i个表达式
        {
            for(int j=0;j<=n;j++)
                for(int k=0;k+j<=n;k+=i)//k是指第 i个表达式中参数的指数 每次加i
            {
                b[j+k]+=a[j];
            }
            for(int j=0;j<=n;j++)
            {
                a[j]=b[j];
                b[j]=0;//给b重新赋值为0  b记录的是每个表达式中的值
            }
        }
        printf("%d\n",a[n]);
    }
    return 0;
}


hdu 1398 

把连续的整数值 替换成连续整数的平方  

所以在k的循环中 变化的值需要修改  增加的是一个数的平方

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100
#define INF 0x7fffffff
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int a[310],b[310];

int main()
{
freopen("ceshi.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)  break;
        for(int i=0;i<=n;i++)
        {
            a[i]=1;
            b[i]=0;
        }
        for(int i=2;i<=n;i++)
        {
            for(int j=0;j<=n;j++)
                for(int k=0;k+j<=n;k+=i*i)
            {
//                cout<<"jjj "<<j<<"  "<<"kkk "<<k<<endl;
                b[j+k]+=a[j];
            }
            for(int j=0;j<=n;j++)
            {
                a[j]=b[j];
                b[j]=0;
            }
        }

        printf("%d\n",a[n]);
    }
    return 0;
}


hdu 1085 

给出 1元 2元 5元硬币的个数  求出这些硬币不能组合得到的数的最小值

还是记录有那些数可以通过这些硬币组合得到 最后找出不能组合得到的最小值

最小到大枚举这些硬币的组合  枚举过程中要注意 范围的变化

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  100100
#define INF 0x7fffffff
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int a[8010],b[8010];

int main()
{
//freopen("ceshi.txt","r",stdin);
    int n1,n2,n3;
    while(scanf("%d%d%d",&n1,&n2,&n3)!=EOF)
    {
        if(n1==0&&n2==0&&n3==0)
            break;
        MEM(a,0);
        MEM(b,0);
        for(int i=0;i<=n1;i++)
            a[i]=1;
        for(int i=0;i<=n1;i++)
            for(int j=0;j<=n2*2;j+=2)//注意范围的变化
                b[i+j]+=a[i];
        for(int i=0;i<=n1+n2*2;i++)
        {
            a[i]=b[i];
            b[i]=0;
        }
        for(int i=0;i<=n1+n2*2;i++)
            for(int j=0;j<=n3*5;j+=5)
                b[i+j]+=a[i];
        for(int i=0;i<=n1+n2*2+n3*5;i++)
        {
            a[i]=b[i];
            b[i]=0;
        }
        int x;
        for(x=0;x<=n1+n2*2+n3*5;x++)
        {
            if(a[x]==0)
            {
                printf("%d\n",x);
                break;
            }
        }
        if(x==(n1+n2*2+n3*5+1))
            printf("%d\n",x);
    }
    return 0;
}


hdu 1171

给出一些不同的值和这些值的个数  然后需要将这些值进行划分 分成两部分

尽量使得两部分的值相等

首先枚举这些值能够组成的所有的值的情况  然后 将和分成两部分 一直向下找 看哪个值是能够被组成的

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>

#define eps 1e-8
#define op operator
#define MOD  10009
#define MAXN  250010
#define INF 0x7fffffff
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int v[60],cnt[60];
int a[MAXN],b[MAXN];

int main()
{
//freopen("ceshi.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n<0)  break;
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&v[i],&cnt[i]);
            sum+=v[i]*cnt[i];
        }
        MEM(a,0);
        MEM(b,0);
        for(int i=0;i<=v[0]*cnt[0];i+=v[0])
            a[i]=1;
        int num=v[0]*cnt[0];
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<=num;j++)
                for(int k=0;k<=cnt[i];k++)
                {
                    b[j+k*v[i]]+=a[j];
                }
            num+=v[i]*cnt[i];
            for(int j=0;j<=num;j++)
            {
                a[j]=b[j];
                b[j]=0;
            }
        }
        for(int i=sum/2;i>=0;i--)
        {
            if(a[i]!=0)
            {
                printf("%d %d\n",sum-i,i);
                break;
            }
        }
    }
    return 0;
}



  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值