注意事项:
这道题的思考方式和完全背包很像,可以参考我写的完全背包的dp解析:java—dp动态规划—完全背包
题目:
一个正整数 n 可以表示成若干个正整数之和,形如:n = n1+n2+…+nk,其中 n1 ≥ n2 ≥…≥ nk, k ≥ 1
我们将这样的一种表示称为正整数 n 的一种划分
现在给定一个正整数 n,请你求出 n 共有多少种不同的划分方法
共一行,包含一个整数 n
共一行,包含一个整数,表示总划分数量
(由于答案可能很大,输出结果请对 109+7 取模)
1 ≤ n ≤ 1000
输入:
5
输出:
7
public class 计数类dp_整数划分 {
public static int N = 1010, mod = 1000000007, n;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
base();
}
//基础版,二维数组三循环
public static void base() {
int[][] f = new int[N][N];
f[0][0] = 1; //从0中选0,是一种方案
for (int i = 1; i<=n; i++) { //枚举从1-n的所有数i
for (int j = 0; j<=n; j++) { //枚举从1-n的所有最终结果j
for (int k = 0; j >= k*i; k++) { //枚举对于当前j,选k个i,切记计数dp是+=更新而不是=
f[i][j] = (f[i][j] + f[i-1][j - k*i]) % mod;
}
}
}
System.out.println(f[n][n]);
}
//优化版,一维数组双循环
public static void op() {
int[] f = new int[N];
f[0] = 1; //还是0选0为1
for (int i = 1; i<=n; i++) {
//直接切掉一维,并且根据状态转移式:f[i][j] = f[i-1][j] + f[i][j-i]
//可以得知状态是从本层(i)转移而来,所以从小到大枚举状态即可
for (int j = i; j<=n; j++) {
f[j] = (f[j] + f[j - i]) % mod;
}
}
System.out.println(f[n]);
}
}
思路:
经典y式dp法
1.状态表示
f[i][j]:从1-i中选择,总和(想成体积也可以)恰好为 j 的方案数量 (count)
2.状态计算
和完全背包问题类似
1.如果不选第 i 个数,那么就直接将 f[i-1][j] 转移过来即可
2.如果选第 i 个数,并且考虑 i 选1-k个,条件是k * i <= j
总和就应该为 f[i][j] = f[i-1][j] + f[i-1][j-i] + f[i-1][j-2i] + ... + f[i][j-ki]
而我们发现 f[i][j-i] = f[i-1][j-i] + f[i-1][j-2i] + ... + f[i][j-ki]
所以可以直接等价替换,f[i][j] = f[i−1][j] + f[i][j−i] (看好了这里是 j-i 不是减1啊)
借一下Wondery大佬的图
声明:
算法思路来源为y总,详细请见https://www.acwing.com/
本文仅用作学习记录和交流