整数划分
一个正整数 n𝑛 可以表示成若干个正整数之和,形如:𝑛=𝑛1+𝑛2+…+𝑛𝑘,其中 𝑛1≥𝑛2≥…≥𝑛𝑘, 𝑘≥1。
我们将这样的一种表示称为正整数 𝑛 的一种划分。
现在给定一个正整数 𝑛,请你求出 𝑛 共有多少种不同的划分方法。
输入格式
共一行,包含一个整数 𝑛。
输出格式
共一行,包含一个整数,表示总划分数量。
由于答案可能很大,输出结果请对 10^9^+7 取模。
数据范围
1≤𝑛≤1000
输入样例:
5
输出样例:
7
思路:把1,2,3, … n分别看做n个物体的体积,这n个物体均无使用次数限制,问恰好能装满总体积为n的背包的总方案数(完全背包问题变形)
初值问题:
求最大值时,当都不选时,价值显然是 0
而求方案数时,当都不选时,方案数是 1(即前 i 个物品都不选的情况也是一种方案),所以需要初始化为 1
即:for (int i = 0; i <= n; i ++) f[i][0] = 1;
等价变形后: f[0] = 1
状态计算:
f[i][j]
表示前i个整数(1,2…,i)恰好拼成j的方案数
求方案数:把集合选0个i,1个i,2个i,…全部加起来
f[i][j] = f[i - 1][j] + f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
f[i][j - i] = f[i - 1][j - i] + f[i - 1][j - 2 * i] + ...;
因此 f[i][j]=f[i−1][j]+f[i][j−i]
; (这一步类似完全背包的推导)
朴素做法
// f[i][j] = f[i - 1][j] + f[i][j - i]
#include <iostream>
using namespace std;
const int N = 1e3 + 7, mod = 1e9 + 7;
int f[N][N];
int main() {
int n;
cin >> n;
for (int i = 0; i <= n; i ++) {
f[i][0] = 1; // 容量为0时,前 i 个物品全不选也是一种方案
}
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= n; j ++) {
f[i][j] = f[i - 1][j] % mod; // 特殊 f[0][0] = 1
if (j >= i) f[i][j] = (f[i - 1][j] + f[i][j - i]) % mod;
}
}
cout << f[n][n] << endl;
}
等价变形
// f[i][j] = f[i - 1][j] + f[i][j - i]
#include <iostream>
using namespace std;
const int N = 1e3 + 7, mod = 1e9 + 7;
int f[N];
int main() {
int n;
cin >> n;
f[0] = 1; // 容量为0时,前 i 个物品全不选也是一种方案
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;
}
完全背包解法 O(n^3^)
Java 代码
public class Main{
public static final int N = 1010;
public static final int M = (int) (1e9 + 7);
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[][] dp = new int[N][N];
dp[0][0] = 1;//一个数都不选是一种方案
for(int i=1; i<=n; i++){
for(int j=0; j<=n; j++){
for(int k=0; j - k * i >= 0; k++){
dp[i][j] += dp[i - 1][j - k * i];
dp[i][j] %= M;
}
}
}
System.out.println(dp[n][n]);
}
}
完全背包解法 - 优化 f[i][j] = f[i-1][j] + f[i-1][j-i] + f[i-1][j-2i] + ... + f[i-1][j-si] f[i][j-i] = f[i-1][j-i] + f[i-1][j-2i] + ... + f[i-1][j-si] 替换:f[i][j] = f[i-1][j] + f[i][j-1] 优化一维:f[j] = f[j] + f[j-i]
Java 代码
public class Main{
public static final int N = 1010;
public static final int M = (int) (1e9 + 7);
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] dp = new int[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]) % M;
}
}
System.out.println(dp[n]);
}
}
计数类DP
public class Main{
public static final int N = 1010;
public static final int M = (int) (1e9 + 7);
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[][] dp = new int[N][N];
dp[0][0] = 1;
for(int i=1; i<=n; i++){
for(int j=1; j<=i; j++){
dp[i][j] = (dp[i - 1][j - 1] + dp[i - j][j]) % M;
}
}
int res = 0;
for(int i=1; i<=n; i++){
res = (res + dp[n][i]) % M;
}
System.out.println(res);
}
}