题目
一个正整数 可以表示成若干个正整数之和,形如: ,其中 。
我们将这样的一种表示称为正整数 的一种划分。
现在给定一个正整数 ,请你求出 共有多少种不同的划分方法。
输入格式
共一行,包含一个整数 。
输出格式
共一行,包含一个整数,表示总划分数量。
由于答案可能很大,输出结果请对 取模。
数据范围
输入样例:
5
输出样例:
7
解题
方法一:动态规划
思路
用多重背包问题的思路考虑:
有 个物品与一个容量为 的背包,第 种物品最多有 件,每件体积是 ( 从 开始),求把背包正好填满的方案数(不考虑顺序)。
思维过程:
动态规划:
- 状态定义: 表示所有只考虑前 个物品,且总体积恰好等于 的所有选法的数量()。
- 状态转移方程: 。
- 初始状态:只考虑前 个物品背包容量为 时只有一种固定的方案,所以:。
代码
import java.util.*;
import java.io.*;
public class Main {
static final int MOD = (int) 1e9 + 7;
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int n = (int) in.nval;
int[][] dp = new int[n + 1][n + 1];
dp[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= n; ++j) {
for (int k = 0; k <= n && k * i <= j; ++k) {
dp[i][j] = (dp[i][j] + dp[i - 1][j - k * i]) % MOD;
}
}
}
System.out.println(dp[n][n]);
}
}
优化
优化的思路和 完全背包问题 - 优化 一样,都是展开公式、代换。
原式:f[i][j] = f[i][j] + f[i-1][j-k*i]
展开:f[i][j] = f[i-1][j] + f[i-1][j-i] + f[i-1][j-2*i] + ... + f[i-1][j-k*i]
f[i][j-i] = f[i-1][j-i] + f[i-1][j-2*i] + ... + f[i-1][j-k*i]
代换:f[i][j] = f[i-1][j] + f[i][j-i]
于是有状态转移方程: 。
import java.util.*;
import java.io.*;
public class Main {
static final int MOD = (int) 1e9 + 7;
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int n = (int) in.nval;
int[][] dp = new int[n + 1][n + 1];
dp[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= n; ++j) {
dp[i][j] = dp[i - 1][j];
if (j >= i) dp[i][j] = (dp[i][j] + dp[i][j - i]) % MOD;
}
}
System.out.println(dp[n][n]);
}
}
滚动数组再优化
import java.util.*;
import java.io.*;
public class Main {
static final int MOD = (int) 1e9 + 7;
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int n = (int) in.nval;
int[] dp = new int[n + 1];
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;
}
}
System.out.println(dp[n]);
}
}
#include <iostream>
using namespace std;
const int N = 1010, MOD = 1e9 + 7;
int n;
int dp[N];
int main() {
scanf("%d", &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;
}
}
printf("%d\n", dp[n]);
return 0;
}
方法二:另一种思路的动态规划
思路
思维过程:
动态规划:
- 状态定义: 表示所有总和是 ,且恰好用了 个数的方案的数量( )。
- 状态转移方程: 。
- 初始状态:。
要求的是所有总和为 ,且恰好用了任意个数的方案的数量,即:。
代码
import java.util.*;
import java.io.*;
public class Main {
static final int MOD = (int) 1e9 + 7;
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int n = (int) in.nval;
int[][] dp = new int[n + 1][n + 1];
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]) % MOD;
}
}
int ans = 0;
for (int i = 1; i <= n; ++i) ans = (ans + dp[n][i]) % MOD;
System.out.println(ans);
}
}
#include <iostream>
using namespace std;
const int N = 1010, MOD = 1e9 + 7;
int n;
int dp[N][N];
int main() {
scanf("%d", &n);
dp[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
dp[i][j] = dp[i - 1][j - 1];
if (j <= i) dp[i][j] = (dp[i][j] + dp[i - j][j]) % MOD;
}
}
int ans = 0;
for (int i = 1; i <= n; ++i) ans = (ans + dp[n][i]) % MOD;
printf("%d\n", ans);
return 0;
}
评论区