文章目录
0. 前言
相关:
1. 分组背包+求方案数
重点: 分组背包、求方案数
注意:
- 其实是分组背包的简单应用题,只不过输出路径我真的很不熟练…
- 将每个公司看成一个组,每个公司组内分配不同的机器看作物品,所分配的该机器数量决定了它的价值
- 状态表示:
f[i][j]
:从第i
个公司中选,设备数量不超过j
的方案的最大价值
- 状态转移方程:
f[i][j]=max(f[i-1][j], f[i-1][j-k]+w[i][k])
,其中,f[i-1][j]
代表一个都不选,这个状态肯定存在。j
为枚举体积,k
枚举是哪个机器,在此第k
个机器也代表了它占k
个体积,顺便用w[i][k]
就能取到它的价值
- 求方案:
- 逆序枚举,确定最优解的上一个转移状态是哪个。这样就能确定出最优解当前选择的机器编号和数量是多少了,也就得到了转移路径
详细步骤代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 15, M = 20;
int n, m;
int w[N][M];
int f[N][M];
int way[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> w[i][j];
for (int i = 1; i <= n; ++i) // 枚举组
for (int j = 0; j <= m; ++j) { // 枚举体积
f[i][j] = f[i - 1][j]; // 一个都不选的情况,在本题其实不用考虑该情况
for (int k = 1; k <= j; ++k) // 枚举物品,数量和价值在此统一到一起表示
f[i][j] = max(f[i][j], f[i - 1][j - k] + w[i][k]);
}
cout << f[n][m] << endl;
int j = m;
for (int i = n; i; --i) { // 反推转移路径,第i组
for (int k = 0; k <= j; ++k) // 选第几个
if (f[i][j] == f[i - 1][j - k] + w[i][k]) {
way[i] = k;
j -= k;
break;
}
}
for (int i = 1; i <= n; ++i) cout << i << ' ' << way[i] << endl;
return 0;
}
在此,有 dfs
、递归两种写法。参考大佬题解:1013. 机器分配(dfs、求方案) 里面是贼精简的代码…
递归代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 15, M = 20;
int n, m;
int w[N][M];
int f[N][M];
int way[N][M];
// 递归求方案
void print(int x, int y) {
if (x == 0) return ;
int k = way[x][y];
print(x - 1, y - k);
cout << x << ' ' << k << endl;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
cin >> w[i][j];
for (int i = 1; i <= n; ++i) // 枚举组
for (int j = 0; j <= m; ++j) { // 枚举体积
f[i][j] = f[i - 1][j]; // 一个都不选的情况,在本题其实不用考虑该情况
for (int k = 1; k <= j; ++k) // 枚举物品,数量和价值在此统一到一起表示
if (f[i][j] <= f[i - 1][j - k] + w[i][k]) {
f[i][j] = max(f[i][j], f[i - 1][j - k] + w[i][k]);
way[i][j] = k; // 存i公司分配j台机器的最优解时第i个公司分配了几台机器
}
}
cout << f[n][m] << endl;
print(n, m);
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= m; ++j)
cout << way[i][j] << ' ';
cout << endl;
}
return 0;
}