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


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


    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
    ",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
    ",count);
            for(i=0;i<count;i++)
            {
               if(i!=count-1)printf("%d ",kk[i]);
               else printf("%d
    ",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;
    }
    


  • 相关阅读:
    linux定时器的使用
    6410 spi总线
    6410 spi 设备驱动
    qtcreator 安装后不能显示菜单
    基于Tiny 6410的内核移植 (NAND FLASH 、UBIFS篇)
    有关ajax跨域问题
    sqlmap的安装
    C# 连接数据库的配置方法
    初识Markdown
    ASP.NET 使用Ueditor富文本编辑器
  • 原文地址:https://www.cnblogs.com/yezekun/p/3925806.html
Copyright © 2020-2023  润新知