题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1028
题意:整数拆分,拆分成正整数的和的方案数。
解题方案:母函数或dp。
方法一:母函数。与上一篇博客类似,属于讨论到的第二种情况。复杂度O(n^3)。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define FOR(i,k,n) for(int i=k;i<n;i++)
#define FORR(i,k,n) for(int i=k;i<=n;i++)
#define scan(a) scanf("%d",&a)
#define scann(a,b) scanf("%d%d",&a,&b)
#define scannn(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define mst(a,n) memset(a,n,sizeof(a))
#define ll long long
#define N 121
#define mod 1000000007
#define INF 0x3f3f3f3f
const double eps=1e-8;
const double pi=acos(-1.0);
ll a[N];//存储多项式相乘的结果,下标为指数
ll tmp[N];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n;
while(~scan(n))
{
mst(a,0);
a[0]=1;//在多项式乘法链的首部添加辅助多项式(1+0*x^1+0*x^2+...+0*x^50)
FORR(i,1,n)
{
mst(tmp,0);
FORR(j,0,n)
{
for(int k=0;j+k<=n;k+=i)
{
tmp[j+k]+=a[j];//此处数组a存着前i-1个多项式相乘的结果
}
}
FORR(j,0,n) a[j]=tmp[j];
}
printf("%lld\n",a[n]);
}
return 0;
}
方法二:dp。dp[i][j]表示i用不大于j的数组成的方案数。复杂度O(n^2)。
先贴出来一个错误的写法:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define FOR(i,k,n) for(int i=k;i<n;i++)
#define FORR(i,k,n) for(int i=k;i<=n;i++)
#define scan(a) scanf("%d",&a)
#define scann(a,b) scanf("%d%d",&a,&b)
#define scannn(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define mst(a,n) memset(a,n,sizeof(a))
#define ll long long
#define N 121
#define mod 1000000007
#define INF 0x3f3f3f3f
const double eps=1e-8;
const double pi=acos(-1.0);
ll dp[N][N];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
FOR(i,1,N)
{
FORR(j,1,i)
{
if(i==1||j==1)
dp[i][j]=1;
else if(i==j)
dp[i][j]=dp[i][j-1]+1;
else
dp[i][j]=dp[i][j-1]+dp[i-j][j];//i-j可能会小于j
}
}
int n;
while(~scan(n))
printf("%lld\n",dp[n][n]);
return 0;
}
因为在递推的过程中,可能会访问到dp[i][j],i小于j的情况,此时dp[i][j]应该等于dp[i][i],而不是0,所以上面的代码错了,还要加一条i<j的分支。
正确的代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define FOR(i,k,n) for(int i=k;i<n;i++)
#define FORR(i,k,n) for(int i=k;i<=n;i++)
#define scan(a) scanf("%d",&a)
#define scann(a,b) scanf("%d%d",&a,&b)
#define scannn(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define mst(a,n) memset(a,n,sizeof(a))
#define ll long long
#define N 121
#define mod 1000000007
#define INF 0x3f3f3f3f
const double eps=1e-8;
const double pi=acos(-1.0);
ll dp[N][N];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
FOR(i,1,N)
{
FORR(j,1,N)
{
if(i==1||j==1)
dp[i][j]=1;
else if(i==j)
dp[i][j]=dp[i][j-1]+1;
else if(i<j)
dp[i][j]=dp[i][i];
else
dp[i][j]=dp[i][j-1]+dp[i-j][j];//dp[i][j-1]为i不用j组合的方案数,dp[i-j][j]为i用j(至少用一个)组合的方案数
}
}
int n;
while(~scan(n))
printf("%lld\n",dp[n][n]);
return 0;
}