题意
输入一个整数n,要求输出n的分拆数。
思路一
母函数,要求n的分拆数,就相当于有n种面值的钱币各无数张,面值为1~n,问你用这些钱币组成n元的方案数。
设用n种面值的钱币组成k元的方案数为ak,则序列a0,a1,a2…ak…an的生成函数为
g(x)=a0+a1*x+a2*x^2+…+ak*x^k+…+an*x^n+……
同时
g(x)=(1+x+x^2+x^3+…+x^n)(1+x^2+x^4+…)(1+x^3+x^6+…)…(1+x^n)…
本题所求的答案即为g(x)中x^n的系数an。
#include <iostream>
using namespace std;
const int maxn = 130;
// c1是保存各项质量砝码可以组合的数目
// c[i]最终结果中代表x^i的系数
// c2是中间量,保存每一次的情况
int c1[maxn], c2[maxn];
int main()
{
int n;
int i, j, k;
while(cin >> n)
{
for(i=0; i<=n; ++i)
{
c1[i] = 1;
c2[i] = 0;
}
for(i=2; i<=n; ++i)
{
for(j=0; j<=n; ++j)
for(k=0; k+j<=n; k+=i)
c2[j+k] += c1[j];
for(j=0; j<=n; ++j)
{
c1[j] = c2[j];
c2[j] = 0;
}
}
cout << c1[n] << endl;
}
return 0;
}
思路二
线性dp,二维数组dp[n][m],其中n为要划分的正整数,m是划分中的最大加数(当m > n时,最大加数为n)
1.首先, dp[i][1]和dp[1][j]=1,不难理解
2.下面看一看m 和 n的关系。它们有三种关系
(1) m > n
在整数划分中实际上最大加数不能大于n,因此在这种情况可以等价为dp[n][n];
(2) m = n
这种情况可用递归表示为dp[n][m-1] + 1,从以上例子中可以看出,就是最大加
数为m和小于m的划分之和
(3) m < n
这是最一般的情况,在划分的大多数时都是这种情况。
dp[n][m]=dp[n][m-1]+dp[n-m][m]
#include<iostream>
using namespace std;
const int maxn=130;
int dp[maxn][maxn];
int main()
{
int n,i,j;
for(i=0;i<=120;i++)
{
dp[i][1]=1;
dp[1][i]=1;
}
for(i=2;i<=120;i++)
for(j=2;j<=120;j++)
{
if(j>i)
dp[i][j]=dp[i][i];
if(j==i)
dp[i][j]=dp[i][j-1]+1;
if(j<i)
dp[i][j]=dp[i][j-1]+dp[i-j][j];
}
while(cin>>n)
cout<<dp[n][n]<<endl;
return 0;
}
思路三
背包的思想,我们用dp[n]表示用这些硬币组成n的方法总数,然后随着钱币种类的增加来更新dp[]的值,也就是最外面的一层循环for(i :1–>n)开始初始化的时候没有钱币,然后新来了面值为1的钱币,接着又来了面值为2的钱币。
显然,每新增加一种面值的钱币,dp[]的值一定在增加,新的dp[] = 未新增前的dp[] + dp[1件新增钱币] + dp[2件新增钱币] + dp[3件新增钱币] +…….dp[k件新增钱币]
由于for里用了完全背包里的顺序,for(j = c[i]; j <= n; j++),所以dp[j] += dp[j - c[i]];中的dp[j - c[i]]已经是有k件新增钱币的状态了。
#include<iostream>
using namespace std;
const int maxn=130;
int dp[maxn];
int main()
{
int n,i,j;
dp[0]=1;
for(i=1;i<=120;i++)
for(j=i;j<=120;j++)
dp[j]+=dp[j-i];
while(cin>>n)
cout<<dp[n]<<endl;
return 0;
}