题意:给定一个数 n ,求不超过 n 的数组成 n 的方案数,例如:
4 = 4;
4 = 3 + 1;
4 = 2 + 2;
4 = 2 + 1 + 1;
4 = 1 + 1 + 1 + 1;
共五种方案。
解法一:看题目求组合数就先想到了生成函数,这是个裸题,学过生成函数的就很好做了。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int main()
{
const int N = 120+5;
int a[N],b[N],n;
while(~scanf("%d",&n))
{
memset(a,0,sizeof(a));
a[0]=1;
for(int i=1;i<=n;i++)
{
memset(b,0,sizeof(b));
for(int j=0;j<=n;j++)
for(int k=0;j+k*i<=n;k++)
b[j+k*i]+=a[j];
memcpy(a,b,sizeof(b));
}
printf("%d\n",a[n]);
}
return 0;
}
解法二:DP 做法,这题也可以算作比较水的背包 DP,设 dp[n][m]为不超过 m 的数组成 n 的方案数,则根据题意以及n,m的相对关系我们把转移方程分成三类:
①:n<m dp[n][m]=dp[n][n]; //根据题意,组成n的数肯定不能超过 n
②:n=m dp[n][m]=dp[n][m-1]+1; //显而易见,由 n 组成的 n 的方案只有一种
③:n>m dp[n][m]=dp[n][m-1]+dp[n-m][m];
//【不超过 m 的数组成 n 的方案数】 =【不超过 m-1 的数组成 n 的方案数】+ 【不超过 m 的数且包含 m 组成 n 的方案数】
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int main()
{
const int N = 120+5;
int dp[N][N],b[N],n;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if(i<j) dp[i][j]=dp[i][i];
else if(i==j) dp[i][i]=dp[i][i-1]+1;
else dp[i][j]=dp[i-j][j]+dp[i][j-1];
}
printf("%d\n",dp[n][n]);
}
return 0;
}