计数类DP
计数类DP?
求方案数的呗
Acwing.900 整数划分
题目概览
一个正整数 n n n可以表示成若干个正整数之和,形如: n = n 1 + n 2 + . . . + n k n = n_1 + n_2 + ...+n_k n=n1+n2+...+nk,其中 n 1 > = n 2 > = . . . > = n k n_1 >= n_2 >= ... >= n_k n1>=n2>=...>=nk, k > = 1 k >= 1 k>=1。
我们将这样的一种表示称为正整数 n n n 的一种划分。
现在给定一个正整数 n n n,请你求出 n n n 共有多少种不同的划分方法。
输入格式
共一行,包含一个整数 n n n。
输出格式
共一行,包含一个整数,表示总划分数量。
由于答案可能很大,输出结果请对 1 0 9 + 7 10^9 + 7 109+7取模。
数据范围
$ 1 <= n <= 1000 $
输入样例:
5
输出样例:
7
举个例子
5 = 5
= 4 + 1
= 3 + 2
= 2 + 2 + 1
= 2 + 1 + 1 + 1
= 3 + 1 + 1
= 1 + 1 + 1 + 1 + 1
懂了?
方法与解:
(一)背包的思路
分析:
背包的体积为 n n n,物品的质量为 m m m,求恰好装满背包的方案数,即完全背包
那我们不妨用闫氏Dp法分析一下
状态表示:
状态计算:
再利用完全背包的思路优化一下:
f
[
i
]
[
j
]
=
f
[
i
]
[
j
]
+
f
[
i
−
1
]
[
j
−
1
]
+
f
[
i
−
1
]
[
j
−
i
∗
2
]
+
…
+
f
[
i
]
[
j
−
i
∗
s
]
f[i][j] = f[i][j] + f[i - 1][j - 1] + f[i - 1][j - i * 2] + …+f[i][j - i * s]
f[i][j]=f[i][j]+f[i−1][j−1]+f[i−1][j−i∗2]+…+f[i][j−i∗s]
f [ i ] [ j − 1 ] f[i][j - 1] f[i][j−1]= f [ i − 1 ] [ j − 1 ] + f [ i − 1 ] [ j − i ∗ 2 ] + … + f [ i − 1 ] [ j ∗ s ] f[i - 1][j - 1]+ f[i - 1][j - i * 2] + … + f[i - 1][j * s] f[i−1][j−1]+f[i−1][j−i∗2]+…+f[i−1][j∗s]
=> f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − i ] f[i][j] = f[i - 1][j] + f[i][j - i] f[i][j]=f[i−1][j]+f[i][j−i]
优化得 f [ j ] = f [ j ] + f [ j − i ] f[j] = f[j] + f[j - i] f[j]=f[j]+f[j−i]
代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010,mod = 1e9 + 7;
int n,f[N];
int main(){
cin >> n;
f[0] = 1;
for(int i = 1;i <= n;i++)
for(int j = i;j <= n;j++)
f[j] = (f[j] + f[j - i]) % mod;
cout << f[n] << endl;
return 0;
}
(二)计数类Dp
分析:
闫氏Dp法:
代码实现:
#include<bits/stdc++.h>
using namespace std;
const int N = 1010,mod = 1e9 + 7;
int n;
int f[N][N];
int main(){
cin >> n;
f[1][1] = 1;
for(int i = 2;i <= n;i++)
for(int j = 1;j <= i;j++)
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;
cout << res << endl;
return 0;
}