hdoj2069 Coin Change

hdoj2069 Coin Change_第1张图片

依然是生成函数,但是加了一个限制条件:每种方案的硬币总数不能超过100枚


解决的办法是维护一个状态矩阵:A[i][j];[i]的意义是凑成i元,[j]的意义是用j枚硬币来凑;A[i][j]的意义是用j枚硬币凑成i元的解决方案数目

于是多项式乘法中的两项相乘就成了状态转移的过程,例如(1+a1*x+a2*x^2+a3*x^3+...)(1+x^5+...),目前凑成3元有a3种方案,则:


A[3][0] + A[3][1] + A[3][2] + A[3][3] = a3


若a3*x^3与x^5相乘,意味着引进1个5元硬币,于是:

A[8][1]增加了A[3][0]种

A[8][2]增加了A[3][1]种

A[8][3]增加了A[3][2]种

A[8][4]增加了A[3][3]种


最后只要对A[sum][i]求和即可,i属于[0, 100]。

//还是生成函数,但要求硬币总数<=100
#include <cstdio>

#define MAXVAL 256
#define MAXCOIN 256

#define COINLIMIT 100

int coins[5]={1,5,10,25,50};

//[i][j]表示用j个硬币凑成i元有多少种方案
int CurrentAns[MAXVAL][MAXCOIN];
int Temp[MAXVAL][MAXCOIN];

int solutions(int sum)
{
	//初始化
	//也就是描述(1+x+x^2+x^3+...)这个式子,由1元硬币组成的
	for (int i=0; i<=sum; ++i)
	{
		for (int j=0; j<MAXCOIN; ++j)
		{
			CurrentAns[i][j]=0;
			if(i==j) CurrentAns[i][j]=1;	//这个矩阵的对角线元素一定都是1,凑m元用m个硬币,显然只有1种方案;且矩阵在对角线以下才有元素。
		}
	}

	//开始多项式乘法
	for (int i=1; i<5; ++i)	//遍历5,10,25,50四种规格的硬币
	{
		//先把当前的状态表暂存下来
		for (int j=0; j<=sum; ++j)
		{
			for (int k=0; k<MAXCOIN; ++k)
			{
				Temp[j][k]=CurrentAns[j][k];
			}
		}

		int CurVal=coins[i];

		for (int j=1; j*CurVal<=sum; ++j)	//当前参与乘法的项。新增j个硬币,j>=1。
		{
			for (int k=0; k+j*CurVal<=sum; ++k)	//遍历乘号左边的式子的每一项,但并不是乘号左边多项式的所有项都需要和当前参与乘法的项相乘
			{
				/******************
				乘法前的状态:k元,分别可用0,1,2,...,k枚硬币凑出,即[k][0], [k][1], [k][2], ..., [k][k]
				乘法后的状态:k+j*CurVal元,分别可用0+j,1+j,2+j,...,k+j枚硬币凑。
				状态转移。
				******************/
				for (int l=0; /*l+j<=COINLIMIT*/l<=k; ++l)	//这个式子,循环条件l<=k就不对,l+j<=100就对了,何解?因为数组的列数只有110,最初开辟的时候,越界了!哈!把数组开大,列数和行数相等,现在l<=k是对的,证实了我之前的猜测,这里根本不用限制硬币总数。
				{
					CurrentAns[k+j*CurVal][l+j]+=Temp[k][l];
				}
			}
		}
	}

	//在最后得到的表中统计sum元,且总硬币数小于100的solution总共有多少个
	int cnt=0;
	for (int i=0; i<=COINLIMIT; ++i)
	{
		cnt+=CurrentAns[sum][i];
	}

	return cnt;
}

int main()
{
	int n;	
	while (scanf("%d", &n)!=EOF)
	{
		printf("%d\n", solutions(n));
	}
	return 0;
}

你可能感兴趣的:(change,coin,生成函数,母函数,hdoj2069)