目录
完全背包写法
例如
3 = 3
= 2+1
= 1+1+1
4 = 4
= 3+1
= 2+2
= 2+1+1
= 1+1+1+1
这个问题可以转成用n种商品(1~n)其中每种商品的体积就是自己的序号,然后用一个容量是n的背包来存放这些商品,每种物品可以取无数次,问能有多少种装法恰好把背包装满。
这样就把问题转成了类似完全背包的类型,只不过完全背包要求的是最大价值,他的状态选择是max,而整数划分要的是种类,因此考虑的是sum
1.朴素状态表示法
状态表示:f[i][j]表示从1~i种数中组合成j的方法数
状态方程: f[i][j] = f[i-1][j]+f[i-1][j-i]+f[i-1][j-2i]+......
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100;
int f[N][N];//所有从1-i中选总体积恰好是j的选法数量
int main() {
int n;
cin >> n;
f[0][0] = 1; //初始条件0由本身组成
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= n; j++) {
for (int k = 0; k * i <= j; k++)
f[i][j] += f[i - 1][j - k * i];
}
}
cout << f[n][n];
return 0;
}
2.优化写法(1)
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]
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100;
int f[N][N];//所有从1-i中选总体积恰好是j的选法数量
int main() {
int n;
cin >> n;
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= n; j++) {
f[i][j] = f[i - 1][j] + f[i][j - i];
}
}
cout << f[n][n];
return 0;
}
3.优化写法(2)
回滚数组使得二维变成一维
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100;
int f[N];//所有从1-i中选总体积恰好是j的选法数量
int main() {
int n;
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];
}
}
cout << f[n];
return 0;
}
另类方法
状态表示:f[i][j]表示能由j个数相加得到总和是i的组合个数
分成两种情况:(1)最小值为1则f[i-1][j-1]
(2) 最小者大于1则f[i-j][j],因为每个数都严格大于1所以对每个数拿出一个1来,j不变但是i变成i-j
状态方程:f[i][j] = f[i-1][j-1]+f[i-j][j]
一个正整数要想用正整数相加得到那么最多由全1相加,所以最后要的是sum是从1到n个数相加的组合数,因此sum=f[n][1]+f[n][2]+.....f[n][n]
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100;
int f[N][N];//总数是i恰好表示成j个数的相加的个数
int main() {
int n;
cin >> n;
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j - 1] + f[i - j][j];
}
}
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += f[n][i];
}
cout << sum;
return 0;
}