母函数

来源:互联网 发布:屏蔽短信端口发送0000 编辑:程序博客网 时间:2024/05/17 04:07

普通母函数解决的大部分是组合问题

如:有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?每种重量各有几种可能方案?

  (1+x)(1+x^2)(1+x^3)(1+x^4)

=(1+x+x^2+x^4)(1+x^3+^4+x^7)

=1 + x + x^2 + 2*x^3 + 2*x^4 + 2*x^5 + 2*x^6 + 2*x^7 + x^8 + x^9 + x^10

指数是重量,系数是方案数

这就是母函数


母函数的求解过程就是模拟多项式相乘的过程


附两个详细的讲解:http://www.wutianqi.com/?p=596  http://www.cnblogs.com/FCWORLD/archive/2010/10/10/1847218.html


/*hdu 1028母函数   感觉有点动态规划的意思就是分解一个整数的方法数  可以分解成小于等于他的数的和*/#include<stdio.h>int a[130],b[130];int main(){int n,i,j,k;while(scanf("%d",&n)!=EOF){for(i=0;i<130;++i)//所有的数都可以完全由1组成,这是1种方法{a[i]=1;b[i]=0;}for(i=2;i<=n;++i)//表示的是元素    本题中就是一个数可以被那些数字组成  1在前边已经算过了{for(j=0;j<=n;j++)for(k=0;k+j<=n;k+=i)//对于数字j来说,加上k个i,得到一个新的数字,那个这个数字可以通过j得到,所以要加上数字j的方法数b[k+j]+=a[j];for(j=0;j<=n;++j)//数组进行轮换操作{a[j]=b[j];b[j]=0;}}printf("%d\n",a[n]);}return 0;}

/*hdu 2082给出字母的个数,每个字母有个号,是他在字母表里的顺序求这些字母组成的单词的数目,若只是字母顺序的差别,则认为是同一个,单词的字母号的和<=50*/#include<stdio.h>int m[30],a[60],b[60];int main(){int t,i,j,k;scanf("%d",&t);while(t--){for(i=0;i<60;++i)a[i]=b[i]=0;for(i=1;i<=26;++i)scanf("%d",&m[i]);for(i=m[1];i>=0;--i)//其实也可以这样写  a[0]=1;   然后下边从i=1开始循环a[i]=1;//本题这样写只是在初始化的时候代为计算了编号为1的字母的运算   价值为1的都可以这样for(i=2;i<=26;++i){for(j=0;j<=50;++j)for(k=0;(k*i+j)<=50&&k<=m[i];++k)b[k*i+j]+=a[j];for(j=0;j<=50;j++)a[j]=b[j],b[j]=0;}j=0;for(i=1;i<=50;++i)//小于等于50的都算j+=a[i];printf("%d\n",j);}return 0;}

/*hdu 1398一种特殊的货币   其值为1^2--17^2求某数额的不同组成方法的数量*/#include<stdio.h>int a[310],b[310];int main(){int n,i,j,k;while(scanf("%d",&n),n){for(i=0;i<=300;++i)a[i]=1,b[i]=0;for(i=2;i<=17;++i){for(j=0;j<=300;++j)for(k=0;k+j<=300;k+=i*i)b[k+j]+=a[j];for(j=0;j<=300;++j)a[j]=b[j],b[j]=0;}printf("%d\n",a[n]);}return 0;}

/*hdu 10851  2  5  三种硬币给出各自的数量  求 最小的不能有这些硬币组成的金额*/#include<stdio.h>#include<string.h>int a[10000],b[10000];int main(){int x,y,z,i,j;while(scanf("%d%d%d",&x,&y,&z),x+z+y){memset(a,0,sizeof(a));memset(b,0,sizeof(b));for(i=0;i<=x;++i)a[i]=1;//一共就三个  就没有用最外层的循环  这是2毛的硬币for(i=0;i<=8000;++i)for(j=0;i+j*2<=8000&&j<=y;j++)b[i+j*2]+=a[i];for(i=0;i<=8000;++i)a[i]=b[i],b[i]=0;//这是5毛的硬币for(i=0;i<=8000;++i)for(j=0;i+j*5<=8000&&j<=z;j++)b[i+j*5]+=a[i];for(i=0;i<=8000;++i)a[i]=b[i],b[i]=0;i=0;while(a[i]) ++i;printf("%d\n",i);}return 0;}

/*hdu 1059之前是用复合背包写的现在用母函数再写一遍  发现不优化会超时   优化后有几分像背包了题意:有六种珠宝,价值为1~6给定他们的数量   求是否能把这些珠宝分成两份,价值相等*/#include<stdio.h>int m[7];int d[210000];int z,b;int main(){int i,j,k,t=1;while(scanf("%d%d%d%d%d%d",&m[1],&m[2],&m[3],&m[4],&m[5],&m[6]),m[1]+m[2]+m[3]+m[4]+m[6]+m[5]){z=0;for(i=1;i<=6;++i)z+=m[i]*i;if(z%2){printf("Collection #%d:\n",t++);printf("Can't be divided.\n\n");continue;}b=z/2;for(i=0;i<=b;++i) d[i]=0;for(i=0;i<=b&&i<=m[1];++i) d[i]=1;//价值为1的for(i=2;i<=6;++i)//价值2~6的{for(j=b;j>=0;j--)//为什么要反向,下边说{if(d[j]==0) continue;//若这个价值拿不到,那么也不可能通过 这个价值+若干价值为i的珠宝  拿到更多的价值for(k=1;k<=m[i]&&k*i+j<=b;++k){if(d[k*i+j]) break;//下边说d[k*i+j]=1;}}}if(d[b]){printf("Collection #%d:\n",t++);printf("Can be divided.\n\n");}else{printf("Collection #%d:\n",t++);printf("Can't be divided.\n\n");}}return 0;}/*为什么要反向做?看过  背包九讲   的都知道,01背包空间优化的时候也是这样写的,为的是避免多次计算如 价值i+一定数量的价值(代表一定数量的物品)=价值j  j>i  当循环到j的时候  还会通过这增加到  价值k   那么这些物品被算了两次,还可能更多次当反向做的时候  算k的时候j还没有加那些价值   所以不会重复加了为什么==1就break掉?通过价值i加到j,价值j存在,那么>价值j的之前已经通过在j的基础上+若干价值访问过了*/


原创粉丝点击