题意:
一个正整数 n
可以表示成若干个正整数之和,形如:n = n1 + n2 + ... + ng
,其中 n1>n2>...> nk,k > 1
。
我们将这样的一种表示称为正整数 n
的一种划分。
现在给定一个正整数 n
,请你求出 n
共有多少种不同的划分方法。
解法一 计数dp
f[i,j] 的状态表示:
集合:所有总和是 i
,并且恰好表示成 j
个数的和的方案
属性:Count
f[i,j] 的状态计算:
我们以 “最小值是 1
”、“最小值大于 1
” 进行集合划分(不重不漏)
-
当 “最小值是
1
”时,由于此子集中 每一个方案都存在1
,如果将其中的 每一个方案都 “去掉一个1
”,就会转化成 “和为i-1
,并且 恰好表示成i-1
个数的和” 的方案,两者是一一映射的关系,方案数量相同。根据定义,表示为:f[i-1,j-1]
。 -
当 “最小值大于
1
”时,由于其中每个数都是大于1
的,那么我们可以将 每个数都减去1
(减去之后仍然是正整数),就会转化成“和为i-j
,并且 恰好表示成j
个数的和” 的方案,两者是一一映射的关系,方案数量相同。根据定义,表示为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] f[i,j]=f[i−1,j−1]+f[i−j,j]
根据定义,显然最终答案需要将 所有能组成和为 n
的方案数都累加起来:
a
n
s
=
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]
#include<bits/stdc++.h>
using namespace std;
const int N = 1010, mod = 1e9+7;
int n;
int dp[N][N];
int main()
{
cin>>n;
dp[0][0] = 1;
for(int i=1; i<=n; ++i)
{
for(int j=1; j<=n; ++j)
{
if(i>=j) dp[i][j] = (dp[i-1][j-1] + dp[i-j][j]) % mod;
}
}
int res = 0;
for(int i=1; i<=n; ++i) res = (res + dp[n][i]) % mod;
cout<<res<<endl;
return 0;
}
解法一 完全背包求方案数
思想详见:AcWing 1021. 货币系统(完全背包求取方案数量)
#include<bits/stdc++.h>
using namespace std;
const int N = 1010, mod = 1e9+7;
int dp[N];
int n;
int main()
{
cin>>n;
dp[0] = 1;
for(int i=1; i<=n; ++i)
{
for(int j=i; j<=n; ++j)
{
dp[j] = (dp[j] + dp[j-i]) % mod;
}
}
cout<<dp[n]<<endl;
return 0;
}