用母函数解组合问题

1.引言

       母函数是解组合问题的工具,关于母函数的介绍参考百度百科:http://baike.baidu.com/view/2415279.htm

       杭电OJ平台与母函数相关的编程题有1028、1085、1171、1398

       北大OJ平台与母函数相关的编程题有1664

2.HD1028

       这道题的意思就是将一个数n,分为1,2,3…,n的组合,允许重复,求组合的种数。

#include<stdio.h>
int a1[121],a2[121];//系数数组
int main()
{
    int i,j,k,n;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0;i<=n;i++)/*初始化第一个函数,把第一个函数的系数都赋值为1,(1+x^a1+x^2a1+x^3a1+……)*/
		{
			a1[i]=1;
			a2[i]=0;
		}
		for(i=2;i<=n;++i)  //求第i个表达式(1+x^ai+x^2ai+x^3ai+...)   
		{          
			for(j=0;j<=n;++j)
			//j 从0到n遍历,这里j就是(前面i个表达式累乘的表达式)里第j个变量,         
				for(k=0;k+j<=n;k+=i)
				// k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。             
					a2[j+k]+=a1[j];           
			// 每次前i个表达式累乘求完后都把中间变量a2赋值给a1 
			for(j=0;j<=n;++j)          
			{  
				a1[j]=a2[j];               
				a2[j]=0;
			}     
		}        
		printf("%d\n",a1[n]); 
	}   
	return 0;
}  

3.PKU1664

       这道题的意思就是将n个一样的苹果放入,m个一样的盘子,盘子允许空。问有多少种放法。

#include <iostream>
using namespace std;
int main()
{
	int row,i;
	cin >> row;
	int *p=new int[row];
	for(i=0;i<row;i++)
	{
		int apples,dishes;
		int j,k,s;
		cin >> apples;
		cin >> dishes;
		int *p1=new int[apples+1];
		int *p2=new int[apples+1];
		for(j=0;j<=apples;j++)
		{
			p1[j]=1;
			p2[j]=0;
		}
		for (j=2;j<=dishes;j++)
		{
			for(k=0;k<=apples;k++)
				for (s=0;s+k<=apples;s+=j)
					p2[s+k]+=p1[k];
			for (k=0;k<=apples;k++)
			{
				p1[k]=p2[k];
				p2[k]=0;
			}
		}
		p[i]=p1[apples];
		delete [] p1;
		delete [] p2;
	}
	for (i=0;i<row;i++)
		cout << p[i]<<endl;
	delete [] p;
	return 0;
}

4.HD1085

       这一题的意思是有num1个1,num2个2,num5个5,问有这些数字(numX(X=1,2,5)是个数上限),不能组合出的最小值。

int main()
{
	int number[3]={1,2,5};
	int num[3]={0};  //因为循环中要用到循环数i来索引,所以定义为数组
	int i,j,k;
	while (scanf("%d %d %d",&num[0],&num[1],&num[2]),num[0]||num[1]||num[2])
	{
		int max=num[0]+2*num[1]+5*num[2];//最大值
		int *p=(int*)calloc(max+1,sizeof(int));//包括0所以max+1
		int *q=(int*)calloc(max+1,sizeof(int));
//调用calloc是因为calloc初始化为0,malloc不初始化
		for (i=0;i<=num[0];i++)     //初始化第一组
		{
			p[i]=1;
			q[i]=0;
		}
		int array_size=num[0];//循环次数
		for(i=2;i<=NUM;i++)   //一共3个数
		{
			for (j=0;j<=array_size;j++)
				for (k=0;k+j<=array_size+number[i-1]*num[i-1];k+=number[i-1])
					q[k+j]+=p[j];
			array_size+=number[i-1]*num[i-1];
			for (j=0;j<=array_size;j++)
			{
				p[j]=q[j];
				q[j]=0;
			}
		}
		for (i=0;i<=max;i++)
		{
			if (p[i]==0)
				break;
		}
		if (i==max)
			printf("%d\n",max+1);
		else
			printf("%d\n",i);
		free(p);
		free(q);
	}
	return 0;
}

5.HD1398

       这题的意思是有17种硬币,面值分别是1至17的平方,输入一个数(不大于300),求组成这个数的硬币种数。

#include <iostream>
using namespace std;
int const coins[18]={0,1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289};//0作为哨兵
int p[301],q[301]={0};
int main()
{
	int i,j,k;
	int sum,kinds;
	while(cin>>sum,sum!=0)
	{
		for(i=0;i<=sum;i++)		
			p[i]=1;
		for(kinds=1;kinds<=17;kinds++)
			if (kinds*kinds>sum) break;
		for (i=2;i<kinds;i++)       //剪枝,可能有多少种类的硬币
		{	
			for (j=0;j<=sum;j++)
				for (k=0;k+j<=sum;k+=coins[i])
					q[k+j]+=p[j];
			for (j=0;j<=sum;j++)
			{
				p[j]=q[j];
				q[j]=0;
			}
		}
		cout<<p[sum]<<endl;
	}
	return 0;
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值