要求输出选择方案的背包问题,P2066 机器分配
P2066 机器分配 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
有一类背包问题,它不仅让你求出最优解,还有求你输出选择方案(选择过程),
下面便是这样一道例题:
题目描述
总公司拥有高效设备 M 台,准备分给下属的 N 个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这 M 台设备才能使国家得到的盈利最大?求出最大盈利值。其中 M≤15,N≤10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数 M。
输入格式
第一行有两个数,第一个数是分公司数 N,第二个数是设备台数 M。
接下来是一个 N×M 的矩阵,表明了第 i 个公司分配 j 台机器的盈利。
输出格式
第一行为最大盈利值。
接下来 N 行为第 i 分公司分 x 台。
P.S. 要求答案的字典序最小。
输入输出样例
输入 #1复制
3 3 30 40 50 20 30 50 20 25 30
输出 #1复制
70 1 1 2 1 3 1
动态转移的过程就不多讲了,这不是本次的重点,直接看状态转移方程:
f[i][j]=f[i-1][j];
然后
f[i][j]=max(f[i][j],f[i][j-k]+arr[i][k]);
这里的重点是怎么求出方案:
我们可以从后往前找,即从最优解开始往前推
若最优解位置为f[i][j],则他的前一个状态一定满足:
f[i-1][j-k]+arr[k]==f[i][j];
实际上就是价格转移过程的循环逆过来写
本题还需注意一个点:题目要求输出按字典序最小顺序输出
据此,我们能写出如下代码:
#include<iostream>
#include<set>
#include<vector>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<map>
#include<math.h>
using namespace std;
typedef long long LL;
const int N = 20;
int arr[N][N], f[N][N],ans[N];
int n, m;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &arr[i][j]);
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
f[i][j] = f[i - 1][j];
for (int k = 0; k <= j; k++) {
f[i][j] = max(f[i][j], f[i - 1][j - k ] + arr[i][k]);
}
}
}
/*cout << endl;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cout << f[i][j] << " ";
}
cout << endl;
}*/
int a = n, b = m;
while (a) {
int t = 0;
for (int k = 0; k <= b;k++) {
if (f[a][b] - arr[a][k] == f[a - 1][b - k]) {
ans[a] = k;
t= b - k;
}
}
a--;
b = t;
}
printf("%d\n", f[n][m]);
for (int i = 1; i <= n; i++) {
printf("%d %d\n", i, ans[i]);
}
return 0;
}
另一种优化写法(小小的优化)
#include<iostream>
#include<set>
#include<vector>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<map>
#include<math.h>
using namespace std;
typedef long long LL;
const int N = 20;
int arr[N][N], f[N][N], ans[N][N];
int n, m;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &arr[i][j]);
}
}
for (int i = n; i >= 1; i--) {
for (int j = m; j >= 1; j--) {
f[i][j] = f[i + 1][j];
for (int k = j; k >= 0; k--) {
if (f[i][j] < f[i + 1][j - k] + arr[i][k]) {
f[i][j] = f[i + 1][j - k] + arr[i][k];
ans[i][j] = k;
}
}
}
}
printf("%d\n", f[1][m]);
for (int i = 1,j=m; i <= n; i++) {
printf("%d %d\n", i, ans[i][j]);
j -= ans[i][j];
}
return 0;
}