母函数

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

如:有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 1085
1  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的基础上+若干价值访问过了
*/


你可能感兴趣的:(优化,BI,2010)