母函数初期简单入门题练习

母函数,关键是理解多项式和问题的联系,构造合适的多项式。


1. hdu1557:  题意:给n个数,如果其中有m个数之和大于这n个数一半,则这m个数组成这个团体叫“获胜联盟”,这m个数中,若有一个数,去掉它,这个团体就不能成为获胜联盟了,那么这个数为关键加入者。每成为一次关键加入者,则这个数的权利加1,求所有数的权利(输出)。

  思路:求出所有的数组合之和(任意几个数之和)(和小于所有和二分之一即可),然后 枚举n个数,每次加一个数(原来之和小于一半,非获胜联盟),看加入后是否成为获胜联盟,若是,该数权利+该组合方案数。

           母函数模型: (1+x^v[0])*(1+x^v[1])*******(1+x^v[n-1])  (v[i]表示第i个数),(每个数有两种选择,取取或不取,最终指数表示和,其系数表示该和有多少种组合方案)

#include<iostream>
#include<vector>
#include<cstdio>
#include<cstring>
int  b[10000];
int  a[10000];
int quanlizhishu[10000];
using namespace std;
int main()
{
    int t;cin>>t;
    while(t--)
    {

        memset(quanlizhishu,0,sizeof(quanlizhishu));
        int n,i,sum=0;cin>>n;vector<int>v(n);
        for(i=0;i<n;i++)
        {
            scanf("%d",&v[i]);
            sum+=v[i];
        }
        int ii,j,k,t;
        sum=sum/2;                 //求一半即可
        for(ii=0;ii<n;ii++)       //计算每个的权利,跑n次“母函数”,每次剔除自己这个数。
        {
            memset(a,0,sizeof(a));  //不忘记初始化!
            memset(b,0,sizeof(b));  //a,b[i],表示指数为i的系数,b是最终量(目前所有多项式情况),a是中间量.
            b[0]=1;               //b保存最终结果,初始化为开始的时候就一个1(指数为0的)。
          for(i=0;i<n;i++)          //这一遍扫括号,n个
          {
              if(i==ii)continue;        //不加自己。
              for(j=0;j<=sum;j++)       //扫目前已经乘好的多项式所有指数,b
              {
                  if(b[j]==0)continue;      //结果的指数为j的系数如果为0,表示没有该指数的项,跳过。
                  for(k=0,t=0;k+j<=sum&&t<=1;t++,k+=v[i]) //,这一遍,扫的是当前要新乘的括号,k是当前括号里指数,每循环一次乘一次,最多循环2(t控制次数)次,(每个只有2项取或不取),每次指数只有0,和v[i].
                     a[k+j]+=b[j];          //保存中间结果
              }
              for(j=0;j<=sum;j++)    //更新b
              {
                  b[j]=a[j];a[j]=0;
              }
          }
          for(j=0;j<=sum;j++)            //计算权利,加入v[i后,是否成为获胜联盟,
          {
              if(j+v[ii]>sum)quanlizhishu[ii]+=b[j];    //每次权利加上组合方案数
          }
        }
        for(i=0;i<n;i++)
       {
          if(i!=n-1) printf("%d ",quanlizhishu[i]);
          else printf("%d\n",quanlizhishu[i]);
       }
    }
    return 0;
}


2.hdu1028 任意给你一个整数,问有多少种拆分方案。

  母函数典例。(1+x^1+x^2+...)*(1+x^2+x^4....)*(1+x^3+x^6+....)。。。第一个括号取1,指数表示个数,0表示不取,其后依次。

#include<iostream>
#include<cstring>
using namespace std;
int a[200];
int b[200];
int main()
{
    int n;
    while(cin>>n)
    {
        int i,j,k;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
       b[0]=1;
       for(i=1;i<=n;i++)  //n个括号,最多到n,
       {
           for(j=0;j<=n;j++)  //目前多项式
           {
              if(b[j]==0)continue;
              for(k=0;k+j<=n;k+=i)      //新括号i
              {
                  a[j+k]+=b[j];           
              }
           }
           for(j=0;j<=n;j++)
           {
               b[j]=a[j];
               a[j]=0;
           }
       }
       cout<<b[n]<<endl;              //指数为n的个数(系数)。
    }
    return 0;
}


3.hdu1398 平方数和的组合数。基数是平方数即可。无限类型,自己看上限多少。适可而止。

      多项式:(1+x^1+x^2+...)*(1+x^4+x^8....)*(1+x^9+x^18+....)...指数表基数,系数是方案数

#include<iostream>
#include<cstring>
using namespace std;
int a[301];
int b[301];
int v[18];
int main()
{
    int n;int p=1;
    for(p=1;p<18;p++)  //先保存基数。
    {
        v[p]=p*p;
    }
    while(cin>>n&&n)
    {
        int i,j,k;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
       b[0]=1;
       for(i=1;i<18;i++)
       {
           for(j=0;j<=n;j++)
           {
              if(b[j]==0)continue;
              for(k=0;k+j<=n;k+=v[i])
              {
                  a[j+k]+=b[j];
              }
           }
           for(j=0;j<=n;j++)
           {
               b[j]=a[j];
               a[j]=0;
           }
       }
       cout<<b[n]<<endl;
    }
    return 0;
}


hoj1085,有限个基数情况,基数为1,2,5,每个个数为题目输入。

    多项式:(1+x^1+x^2+..)*(1+x^2+x^4+...)*(1+x^5+x^10+..)每个的项数为所给的个数+1(可不取)。响应处理技巧见代码。


#include<iostream>
#include<cstring>
using namespace std;
int a[9001];
int b[9001];
int main()
{
    int n;int x,y,z;

    while(cin>>x>>y>>z&&(x||y||z))
    {
        int i,j,k;int t;
        int min=-1;bool mark=1;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
       b[0]=1;
       int c[4]={0,1,2,5};      //基数数组
       int d[4]={0,x,y,z};         //用一个数组来对应第i个括号项数。
       for(i=1;i<4;i++)
       {
           int maxzhishu;
           if(i==1)maxzhishu=x;             //上限优化,
           else if(i==2)maxzhishu=x+2*y;
           else maxzhishu=x+2*y+5*z;
           for(j=0;j<=maxzhishu;j++) //b[i]中的指数扫一遍.
           {
              for(k=0,t=0;k+j<=maxzhishu&&t<=d[i];k+=c[i],t++)  //取的次数为个数
              {
                  a[j+k]+=b[j];
              }
              if(i==1)break;
           }
           for(j=0;j<=maxzhishu;j++)
           {

               b[j]=a[j];
               if(b[j]==0){min=j;break;mark=0;}  //输出第一个无法实现的组合
               a[j]=0;
           }
           if(mark==0)break;
       }
       if(min==-1)cout<<x+2*y+5*z+1<<endl;
       else       cout<<min<<endl;
    }
    return 0;
}


hdu 1171,每个物品有价值和数量。都由输入给出。把所有物品分给2个人(单件物品不可拆分),要求尽量公平(价值差距尽量小).

母函数可以求出价值和的所有可能情况,然后总价值除以2,从这开始找存在的方案。第一个找到的(离中点最近),是最公平的。

母函数:不贴了。

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int a[500001];
int b[500001];
struct vv
{
    int x;
    int count;
};
int main()
{
    int n;
    while(cin>>n&&n>=0)
    {
        int i,j,k;int t;int marks;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
       b[0]=1;
       vector<vv>v(n+1);
       for(i=1;i<=n;i++)
       {
           cin>>v[i].x>>v[i].count;
       }
       int maxzhishu=0;
       for(i=1;i<=n;i++)
       {
           maxzhishu+=v[i].x*v[i].count;
           for(j=0;j<=maxzhishu;j++) //b[i]中的指数扫一遍.
           {
               if(b[j]==0)continue;    //如果指数为0,不存在该项。
              for(k=0,t=0;k+j<=maxzhishu&&t<=v[i].count;k+=v[i].x,t++)
              {
                  a[j+k]+=b[j];
              }
              if(i==1)break;//第一个括号只要乘一次.
           }
           for(j=0;j<=maxzhishu;j++)
           {
               b[j]=a[j];
               a[j]=0;
           }
       }                                      //此时max指数是所有价值之和。
        for(i=maxzhishu/2;i>=0;i--)         //找到一半之后开始早到第一个有组合数的(存在的方案)
                if(b[i]){ marks=i;break;}   //现在看来这个marks好像没用...
          cout<<maxzhishu-i<<" "<<i<<endl;
    }
    return 0;
}



hdu 2079 有限数量组合。


#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int a[10001];
int b[10001];
struct vv      //有限项组合,每项指数与数量用结构体击杀!
{
    int x;
    int count;
};
int main()
{
    int n;int tt;cin>>tt;
    while(tt--)
    {
        int t;
        int mubiao;cin>>mubiao;
        int i,j,k;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
       b[0]=1;cin>>n;
       vector<vv>v(n+1);
       for(i=1;i<=n;i++)
       {
           cin>>v[i].x>>v[i].count;
       }
       int maxzhishu=0;
       for(i=1;i<=n;i++)
       {
           maxzhishu+=v[i].x*v[i].count;        //最大指数优化
           if(maxzhishu>mubiao)maxzhishu=mubiao;
           for(j=0;j<=maxzhishu;j++)                                   //b[i]中的指数扫一遍.
           {
               if(b[j]==0)continue;                                   //如果指数为0,不存在该项。
              for(k=0,t=0;k+j<=maxzhishu&&t<=v[i].count;k+=v[i].x,t++)
              {
                  a[j+k]+=b[j];
              }
              if(i==1)break;//第一个括号只要乘一次.
           }
           for(j=0;j<=maxzhishu;j++)
           {

               b[j]=a[j];
               a[j]=0;
           }
       }
          cout<<b[mubiao]<<endl;
    }
    return 0;
}





hdu2082,有限,每个有价值,典例。

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int a[101];
int b[101];
struct vv      //有限项组合,每项指数与数量用结构体击杀!
{
    int x;
    int count;
};
int main()
{
    int n;int tt;cin>>tt;
    while(tt--)
    {
        int t;
        int i,j,k;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
       b[0]=1;
       vector<vv>v(27);
       for(i=1;i<=26;i++)
       {
           v[i].x=i;
           cin>>v[i].count;
       }
       int maxzhishu=0;
       for(i=1;i<=26;i++)
       {
           maxzhishu+=v[i].x*v[i].count;
           if(maxzhishu>50)maxzhishu=50;
           for(j=0;j<=maxzhishu;j++)                                   //b[i]中的指数扫一遍.
           {
               if(b[j]==0)continue;                                   //如果指数为0,不存在该项。
              for(k=0,t=0;k+j<=maxzhishu&&t<=v[i].count;k+=v[i].x,t++)
              {
                  a[j+k]+=b[j];
              }
              if(i==1)break;//第一个括号只要乘一次.
           }
           for(j=0;j<=maxzhishu;j++)
           {

               b[j]=a[j];
               a[j]=0;
           }
       }
       int sum=0;
          for(i=1;i<=50;i++)
             if(b[i])sum+=b[i];
          cout<<sum<<endl;
    }
    return 0;
}





hdu 2110,数量有限,价值,


#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
__int64 a[1001];
__int64 b[1001];
struct vv      //有限项组合,每项指数与数量用结构体击杀!
{
    int x;
    int count;
};
int main()
{
    int n;int tt;
    while(cin>>n&&n)
    {
        int t;
        int mubiao=0;
        int i,j,k;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
       b[0]=1;
       vector<vv>v(n+1);
       for(i=1;i<=n;i++)
       {
           cin>>v[i].x>>v[i].count;
           mubiao=mubiao+v[i].x*v[i].count;
       }
       if(mubiao%3!=0){cout<<"sorry"<<endl;continue;}
       mubiao/=3;
       int maxzhishu=0;
       for(i=1;i<=n;i++)
       {
           maxzhishu+=v[i].x*v[i].count;
           if(maxzhishu>mubiao)maxzhishu=mubiao;
           for(j=0;j<=maxzhishu;j++)                                   //b[i]中的指数扫一遍.
           {
               if(b[j]==0)continue;                                   //如果指数为0,不存在该项。
              for(k=0,t=0;k+j<=maxzhishu&&t<=v[i].count;k+=v[i].x,t++)
              {
                  a[j+k]+=b[j];
              }
              if(i==1)break;//第一个括号只要乘一次.
           }
           for(j=0;j<=maxzhishu;j++)
           {

               b[j]=a[j]%10000;
               a[j]=0;
           }
       }
       if(b[mubiao])
          cout<<b[mubiao]<<endl;
       else cout<<"sorry"<<endl;
    }
    return 0;
}





类似:普通型母函数hdu 2152,2189。无非是基数变化,数量变化。

不贴了。


hdu1709 用一个天平称东西,每一定种类的砝码各一个,问哪些重量称不出来,(砝码俩边可放),这题是有负指数母函数,下表不能负,所以每个加100(最大负),总的加100*N+sum。

母函数:(x^-p[0]+1+x^p[0])*(......).....此时,负表放在物品那一边,正数是放在砝码一边,或者不放。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int a[30005];int b[30005];
int main()
{
    int i,j,t,k,n;
    while(scanf("%d",&n)!=EOF)
    {
        int p[105];
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        int sum=0;
         for(i=1;i<=n;i++)
        {
          scanf("%d",&p[i]);
          sum+=p[i];
        }
        b[0]=1;
        for(i=1;i<=n;i++)
        {
            for(j=0;j<=n*100+sum;j++)       //加100处理负数(最多-100)情况,每个加100,最大指数可能n*100+sum.
            {
               if(b[j]==0)continue;
                for(k=-1*p[i]+100,t=0;k+j<=n*100+sum&&t<3;k=k+p[i],t++) //关键,来一个新的都加100.共加了n*100
                     {
                          a[j+k]+=b[j];
                     }
               if(i==1)break;
            }
            for(j=0;j<=n*100+sum;j++)
                {
                    b[j]=a[j];
                    a[j]=0;
                }
        }
        int count=0;int kk[10005];
         memset(kk,0,sizeof(kk));
         for(i=n*100;i<=n*100+sum;i++)
        {
           if(b[i]==0){kk[count]=i-n*100;count++;}
        }
        printf("%d\n",count);
        for(i=0;i<count;i++)
        {
           if(i!=count-1)printf("%d ",kk[i]);
           else printf("%d\n",kk[i]);
        }
    }
    return 0;
}



hdu 1521 n种物品,每种xi个,取r个的排列数,显然这个是排列问题,用指数型母函数(需要先学习指数型母函数)。
该题: (1+x+x^2/2!+x^3/3!+...x^xi/xi!)*(1+...)


#include<iostream>
#include<vector>
#include<cstring>
#include<iomanip>
using namespace std;
double a[50];        //指数型的,系数用double型,
double b[50];
double fa[50];
void f()  //阶乘
{
    fa[0]=1;
     fa[1]=1;
    for(int i=2;i<50;i++)
    fa[i]=fa[i-1]*i;
}
int main()
{
    f();
    int n,r,i,j,k,t;
    while(cin>>n>>r)
    {
        vector<int>v(n+1);
        for(i=1;i<=n;i++)
             cin>>v[i];
        memset(a,0,sizeof(a));  memset(b,0,sizeof(b));
        b[0]=1;
        for(i=1;i<=n;i++)
        {
            for(j=0;j<=r;j++)        //扫指数
               {
                   if(b[j]==0)continue;
                   for(k=0,t=0;k+j<=r&&t<=v[i];k++,t++) //新来项,k次
                   {
                       a[j+k]=a[j+k]+b[j]/fa[k];     //每次 除以次数的阶乘,(不同点一)
                   }
                   if(i==1)break;
               }
               for(j=0;j<=r;j++)
               {
                   b[j]=a[j];
                   a[j]=0;
               }
        }
        cout<<fixed<<setprecision(0)<<b[r]*fa[r]<<endl;   //最终先乘以阶乘,并取整即可(不同点二)
    }
    return 0;
}


posted @ 2014-03-04 21:37  天羽屠龙舞  阅读(185)  评论(0编辑  收藏  举报