每种划分方式都是按照从大到小排序的
样例分析:
5有7种划分方式
5 = 5
5 = 4 + 1
5 = 3 + 2
5 = 3 + 1 + 1
5 = 2 + 2 + 1
5 = 2 + 1 + 1 + 1
5 = 1 + 1 + 1 + 1 + 1
解法一:用背包问题的思想
把整数n看做一个容量为n的背包
然后一共有n个物品,每个物品的体积分别是1,2,3,...,n。(也就是n这个数的所有因子)
每种物品可以用无限次,所以是完全背包问题
求恰好装满背包的方案数
状态表示:
集合:dp[i][j]表示从物品1~i中选,总体积恰好为j的选法的集合
属性:数量
状态计算:集合的划分,同背包问题
根据第i个物品选了几个来划分
状态数量:n ^ 2
转移数量:n(粗略估计的,比n小,应该为log n)
时间复杂度:n ^ 3
然后进行优化
二维做法
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1010, mod = 1e9 + 7; 4 int dp[N][N]; 5 int main() { 6 int n; 7 cin >> n; 8 for (int i = 0; i <= n; i++) { //注意是从0开始 9 dp[i][0] = 1; //容量为0时,前i个物品全不选也是一种方案 10 } 11 for (int i = 1; i <= n; i++) { 12 for (int j = 0; j <= n; j ++) { 13 dp[i][j] = dp[i - 1][j] % mod; //特殊dp[0][0] = 1 14 if (j >= i) { 15 dp[i][j] = (dp[i - 1][j] + dp[i][j - i]) % mod; 16 } 17 } 18 } 19 cout << dp[n][n] << endl; 20 return 0; 21 }
然后再转化为一维,再从小到大循环体积
注意初值问题:
求最大值时,当都不选时,价值显然是 0
而求方案数时,当都不选时,方案数是 1(即前 i 个物品都不选的情况也是一种方案),所以需要初始化为 1
即需要这样:
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
等价变形后: f[0] = 1
一维做法
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1010, mod = 1e9 + 7; 4 int dp[N]; 5 int main() { 6 int n; 7 cin >> n; 8 dp[0] = 1; //一个数都不选,是一种方案 9 for (int i = 1; i <= n; i++) { 10 for (int j = i; j <= n; j++) { 11 dp[j] = (dp[j] + dp[j - i]) % mod; 12 } 13 } 14 cout << dp[n] << endl; 15 return 0; 16 }
解法二:
PS : 最小值1 表示 :j个数的总和是i的数中最小的数为1
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1010, mod = 1e9 + 7; 4 int dp[N][N]; 5 int main() { 6 int n; 7 cin >> n; 8 dp[0][0] = 1; //总和是0,用0个数来表示,是1种方案 9 for (int i = 1; i <= n; i++) { 10 for (int j = 1; j <= i; j++) { //总和是i的话,最多表示成i个数的和 11 dp[i][j] = (dp[i - 1][j - 1] + dp[i - j][j]) % mod; 12 } 13 } 14 int res = 0; 15 for (int i = 1; i <= n; i++) { 16 res = (res + dp[n][i]) % mod; 17 } 18 cout << res << endl; 19 return 0; 20 }