HDU 1028 Ignatius and the Princess III(动态规划+递归+母函数)

本文探讨了一个经典的整数拆分问题,通过动态规划、递归和母函数三种方法来寻找一个正整数N可以被拆分成多少种不同的组合。提供了详细的算法思路和C++代码实现。


在这里插入图片描述

题目描述

“Well, it seems the first problem is too easy. I will let you know how foolish you are later.” feng5166 says.

"The second problem is, given an positive integer N, we define an equation like this:
N=a[1]+a[2]+a[3]+…+a[m];
a[i]>0,1<=m<=N;
My question is how many different equations you can find for a given N.
For example, assume N is 4, we can find:

  • 4 = 4;
  • 4 = 3 + 1;
  • 4 = 2 + 2;
  • 4 = 2 + 1 + 1;
  • 4 = 1 + 1 + 1 + 1;

so the result is 5 when N is 4. Note that “4 = 3 + 1” and “4 = 1 + 3” is the same in this problem. Now, you do it!"

Input
The input contains several test cases. Each test case contains a positive integer N(1<=N<=120) which is mentioned above. The input is terminated by the end of file.

Output
For each test case, you have to output a line contains an integer P which indicate the different equations you have found.

Sample Input
4
10
20

Sample Output
5
42
627
在这里插入图片描述

解题方法

1、动态规划

状态转移方程如下:


d p [ i ] [ j ] = { 1 + d p [ i ] [ j − 1 ] i=j d p [ i ] [ i ] i<j d p [ i − j ] [ j ] + d p [ i ] [ j − 1 ] i>j dp[i][j]=\begin{cases}1+dp[i][j-1]&\text{i=j}\\dp[i][i]&\text{i<j}\\dp[i-j][j]+dp[i][j-1]&\text{i>j}\end{cases} dp[i][j]=1+dp[i][j1]dp[i][i]dp[ij][j]+dp[i][j1]i=ji<ji>j
其中, d p [ i ] [ j ] dp[i][j] dp[i][j]表示用前 j j j种数字来组成数字 i i i所用的方法数。

View Code

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 121;
int dp[MAXN][MAXN];
int main()
{
	for(int i=1;i<MAXN;++i)
	{
		dp[1][i]=1;
		dp[i][1]=1;
	}
	for(int i=2;i<MAXN;++i)
	{
		for(int j=2;j<MAXN;++j)
		{
			if(i==j)
			{
				dp[i][j]=1+dp[i][j-1];
			}
			else if(i<j)
			{
				dp[i][j]=dp[i][i];
			}
			else if(i>j)
			{
				dp[i][j]=dp[i-j][j]+dp[i][j-1];
			}
		}
	}
	int n;
	while(cin>>n)
	{
		cout<<dp[n][n]<<endl;
	}
	return 0;
}

2、递归

用函数 d i v i d e ( i , j ) divide(i,j) divide(i,j)返回用前 j j j中数字组合成数字 i i i的方法数。
i < 1 ∣ ∣ j < 1 i<1||j<1 i<1j<1时,结果为0种
i = = 1 & & j = = 1 i==1\&\&j==1 i==1&&j==1时,结果只有1种
i = = j i==j i==j时,结果为 d i v i d e ( i , j − 1 ) + 1 divide(i,j-1)+1 divide(i,j1)+1
i < j i<j i<j时,结果为 d i v i d e ( i , i ) divide(i,i) divide(i,i)
i > j i>j i>j时,结果为 d i v i d e ( i − j , j ) + d i v i d e ( i , j − 1 ) divide(i-j,j)+divide(i,j-1) divide(ij,j)+divide(i,j1)

View Code

#include <bits/stdc++.h>
using namespace std;

int divide(int i,int j)
{
	if(i<1||j<1)
	{
		return 0;
	}
	if(i==1&&j==1)
	{
		return 1;
	}
	if(i==j)
	{
		return divide(i,j-1)+1;
	}
	if(i>j)
	{
		return divide(i-j,j)+divide(i,j-1);
	}
	if(i<j)
	{
		return divide(i,i);
	}
}

int main()
{
	int n;
	while(cin>>n)
	{
		cout<<divide(n,n)<<endl;
	}
	return 0;
}

直接使用该方法会超时。
解决方法:将所有情况的方法数存到数组中即可。
在这里插入图片描述
AC代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=121;
int a[MAXN]={1,2,3,5,7,11,15,22,30,42,56,77,101,135,176,231,297,385,490,627,792,1002,1255,1575,1958,2436,3010,3718,4565,5604,
6842,8349,10143,12310,14883,17977,21637,26015,31185,37338,44583,53174,63261,75175,89134,105558,124754,147273,173525,204226,
239943,281589,329931,386155,451276,526823,614154,715220,831820,966467,1121505,1300156,1505499,1741630,2012558,2323520,2679689,
3087735,3554345,4087968,4697205,5392783,6185689,7089500,8118264,9289091,10619863,12132164,13848650,15796476,18004327,20506255,
23338469,26543660,30167357,34262962,38887673,44108109,49995925,56634173,64112359,72533807,82010177,92669720,104651419,118114304,
133230930,150198136,169229875,190569292,214481126,241265379,271248950,304801365,342325709,384276336,431149389,483502844,541946240,
607163746,679903203,761002156,851376628,952050665,1064144451,1188908248,1327710076,1482074143,1653668665,1844349560};

int main()
{
	int n;
	while(cin>>n)
	{
		cout<<a[n-1]<<endl;
	}
	return 0;
}

3、母函数

母函数就是一列用来展示一串数字的挂衣架。 ——赫伯特·唯尔夫

母函数的核心:

1.把组合问题的加法法则和幂级数的乘幂对应起来。
2.把离散数列和幂级数一 一对应起来,把离散数列间的相互结合关系对应成为幂级数间的运算关系,最后由幂级数形式来确定离散数列的构造。


构造母函数如下:

G ( x ) = ( 1 + x + x 2 + x 3 + . . . ) ( 1 + x 2 + x 4 + . . . ) ( 1 + x 3 + x 6 + . . . ) + . . . G(x)=(1+x+x^2+x^3+...)(1+x^2+x^4+...)(1+x^3+x^6+...)+... G(x)=(1+x+x2+x3+...)(1+x2+x4+...)(1+x3+x6+...)+...
由此,我们可以将该题转换成几个多项式相乘的问题,我们只要求出 x n x^n xn的系数,就可以得到答案了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=121;
int a[MAXN],temp[MAXN];

int main()
{
	int n;
	while(cin>>n)
	{
		for(int i=0;i<MAXN;++i)
		{
			a[i]=1;
			temp[i]=0;
		}
		for(int i=2;i<=n;++i)
		{
			for(int j=0;j<=n;++j)
			{
				for(int k=0;k+j<=n;k+=i)
				{
					temp[k+j]+=a[j];
				}
			}
			for(int j=0;j<=n;++j)
			{
				a[j]=temp[j];
				temp[j]=0;
			}
		}
		cout<<a[n]<<endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值