题目描述
“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][j−1]dp[i][i]dp[i−j][j]+dp[i][j−1]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<1∣∣j<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,j−1)+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(i−j,j)+divide(i,j−1)
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;
}
本文探讨了一个经典的整数拆分问题,通过动态规划、递归和母函数三种方法来寻找一个正整数N可以被拆分成多少种不同的组合。提供了详细的算法思路和C++代码实现。
1905

被折叠的 条评论
为什么被折叠?



