组合数学 之 母函数2

hdu 1709

就是给定n种质量的砝码  每种质量的砝码只有一个 

求出利用这些砝码 不能称出的重量的个数(重量是这些砝码和的范围之内)

如果个数超过0  就把这些质量的值输出

所以利用这些砝码表示重量的时候既可以相加又可以相减

尤其是可以相减!!!!

<span style="font-family:KaiTi_GB2312;font-size:18px;">#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  10010
#define INF 0x7fffffff
#define MEM(a,x)    memset(a,x,sizeof a)
#define ll __int64

using namespace std;

int a[MAXN],b[MAXN];
int c[110];
int ans[110];

int abs(int x,int y)
{
    if(x>y)
        return x-y;
    return y-x;
}

int main()
{
//freopen("ceshi.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int sum=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&c[i]);
            sum+=c[i];
        }
        sort(c,c+n);
        MEM(a,0);
        MEM(b,0);
        a[0]=1;
        a[c[0]]=1;
        int len=c[0];
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<=len;j++)
            {
                b[j+c[i]]+=a[j];
                b[abs(c[i],j)]+=a[j];
            }
//            for(int j=0;j<=len;j++)
//            {
//                if(a[j]!=0&&c[i]>j)
//                {
//                    b[c[i]-j]+=a[j];
//                }
//            }
//            len+=c[i];
            for(int j=0;j<=len+c[i];j++)
            {
                if(b[j]!=0)
                {
                    a[j]=b[j];
                    b[j]=0;
                }
            }
            len+=c[i];
        }
        int cnt=0;
        for(int i=1;i<=sum;i++)
        {
            if(a[i]==0)
            {
                ans[cnt++]=i;
            }
        }
        if(cnt==0)
        {
            puts("0");
            continue;
        }
        else
        {
            printf("%d\n",cnt);
            for(int i=0;i<cnt;i++)
            {
                if(i==0)
                    printf("%d",ans[i]);
                else
                    printf(" %d",ans[i]);
            }
            puts("");
        }
    }
    return 0;
}
</span>

hdu 2069 Coin Change

题意就是用五种面值的硬币找零  五种面值为50 25 10 5 1

但是有一个限制条件就是所用的硬币数不能超过100

之前只是用一维的来表示 能组成各数值的种类数  现在加了一维  a[i][j]表示用j个硬币表示数值i的种类数

枚举拓展值时 需要根据使用的硬币数来进行修改

#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[260][110],b[260][110];//a[i][j]表示用j个硬币组成i值的种类数
int cnt[5]={1,5,10,25,50};

int main()
{
//freopen("ceshi.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        MEM(a,0);
        MEM(b,0);
        for(int i=0;i<=100;i+=cnt[0])
        {
            a[i][i]=1;
        }
        for(int i=1;i<=4;i++)//控制硬币类型  即多项式的第几项
        {
            for(int j=0;j<=n;j++)//枚举已知数值
            {
                for(int k=0;k+j<=n;k+=cnt[i])//枚举拓展数值
                {
                    for(int l=0;l+k/cnt[i]<=100;l++)
                    {
                        b[j+k][l+k/cnt[i]]+=a[j][l];
                    }
                }
            }
            for(int j=0;j<=n;j++)
                for(int k=0;k<=100;k++)
            {
                a[j][k]=b[j][k];
                b[j][k]=0;
            }
        }
        int sum=0;
        for(int i=0;i<=100;i++)
            sum+=a[n][i];
        printf("%d\n",sum);
    }
    return 0;
}


hdu 2079  选课时间

给定n个学分  k门课  对于同一个学分 所有的课都试做一样的

所以题目类似于给定若干个值以及这些值的个数  问这些值组成n值的方法数有多少

在循环过程中必须遵循的限制是这个值的个数  其次是 如果这时的值大于n就可以不要再继续计算

#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[400],b[400];
int cour[20],cnt[20];

int main()
{
//freopen("ceshi.txt","r",stdin);
    int tc;
    scanf("%d",&tc);
    while(tc--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=0;i<k;i++)
        {
            scanf("%d%d",&cour[i],&cnt[i]);
        }
        MEM(a,0); MEM(b,0);
        for(int i=0;i<=n&&(i<=cour[0]*cnt[0]);i+=cour[0])
        {
            a[i]=1;
        }
        for(int i=1;i<k;i++)
        {
            for(int j=0;j<=n;j++)
                for(int l=0;l<=cnt[i]&&((l*cour[i]+j)<=n);l++)
            {
                b[j+l*cour[i]]+=a[j];
            }
            for(int j=0;j<=n;j++)
            {
                a[j]=b[j];
                b[j]=0;
            }
        }
        printf("%d\n",a[n]);
    }
    return 0;
}

当然也可以只考虑 值的个数的限制(稍微慢一点)

#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[400],b[400];
int cour[20],cnt[20];

int main()
{
//freopen("ceshi.txt","r",stdin);
    int tc;
    scanf("%d",&tc);
    while(tc--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=0;i<k;i++)
        {
            scanf("%d%d",&cour[i],&cnt[i]);
        }
        MEM(a,0); MEM(b,0);
        for(int i=0;(i<=cour[0]*cnt[0]);i+=cour[0])
        {
            a[i]=1;
        }
        for(int i=1;i<k;i++)
        {
            for(int j=0;j<=n;j++)
                for(int l=0;l<=cnt[i];l++)
            {
                b[j+l*cour[i]]+=a[j];
            }
            for(int j=0;j<=n;j++)
            {
                a[j]=b[j];
                b[j]=0;
            }
        }
        printf("%d\n",a[n]);
    }
    return 0;
}


hdu 2152 Fruit

要通过组合得到有M个水果的水果拼盘

现在有n种水果  每种水果最多用A个 最少用B个

问有多少种可能

总的来说还是差不多的 主要的是枚举时的起始量以及最后结束的大小

#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[110],b[110];
int least[110],mx[110];

int main()
{
//freopen("ceshi.txt","r",stdin);
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<n;i++)
            scanf("%d%d",&least[i],&mx[i]);
        MEM(a,0); MEM(b,0);
        for(int i=least[0];i<=mx[0];i++)
            a[i]=1;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<=m;j++)
                for(int k=least[i];k<=mx[i]&&(j+k)<=m;k++)
            {
                b[j+k]+=a[j];
            }
            for(int j=0;j<=m;j++)
            {
                a[j]=b[j];
                b[j]=0;
            }
        }
        printf("%d\n",a[m]);
    }
    return 0;
}



//母函数也做了不少题了  写点感受吧

总的来说  题目类型一般都是给定一些值 以及使用这些值次数的范围 问组成某个数的种类数

最主要的还是那三个嵌套循环   尤其是枚举k的值 在这里容易产生变化 

相对来说  这样的题目不会太难  细心 想好枚举的情况即可


  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值