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

来源:互联网 发布:linux scan 编辑:程序博客网 时间:2024/05/13 01:16

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


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;}


0 0