题目
思路
状态表示f[i,j]f[i,j]f[i,j]
集合
所有总和是iii,并且恰好表示成jjj个数的和的方案数。
属性
数量
状态计算
可以把f[i,j]f[i,j]f[i,j]的所有方案分为两类:
- 方案里的j个数最小值是1
f[i,j]=f[i−1,j−1]f[i,j]=f[i-1,j-1]f[i,j]=f[i−1,j−1]
那满足这个条件的方案数 等价于 把这个最小的1拿走的方案数 - 方案里的j个数最小值大于1
f[i,j]=f[i−j,j]f[i,j]=f[i-j,j]f[i,j]=f[i−j,j]
那满足这个条件的方案数 等价于 把所有j个数都减去1的方案数
所以状态转移方程为:f[i,j]=f[i−1,j−1]+f[i−j,j]f[i,j]=f[i-1,j-1]+f[i-j,j]f[i,j]=f[i−1,j−1]+f[i−j,j]
dp初始状态
f[0,0]=1f[0,0]=1f[0,0]=1
0个数组成的和为1的方案只有1个
最终结果
ans=f[n,1]+f[n,2]+...f[n,n]ans=f[n,1]+f[n,2]+...f[n,n]ans=f[n,1]+f[n,2]+...f[n,n]
所有方案数等于分别用1个、2个、…、n个数组成数n的方案数的和
代码
#include<iostream>
using namespace std;
const int N=1010,mod=1e9+7;
int n;
int f[N][N];
int main()
{
scanf("%d",&n);
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++) //j最大不超过i,因为拆分的最小值是1,不会再小了
f[i][j]=(f[i-1][j-1]+f[i-j][j])%mod;
int res=0;
for(int i=1;i<=n;i++)
res=(res+f[n][i])%mod;
printf("%d",res);
return 0;
}