原题链接:https://www.luogu.com.cn/problem/P2066
难度:普及/提高-
题意分析
转化一下。有 n n n 个公司,对于第 i i i 个公司,有 m m m 件机器可供选择, 选择第 j j j 台机器的代价是数量加1,价值是 w [ i ] [ j ] w[i][j] w[i][j],所有公司选择的总代价不得超过 m m m。要求求出最大价值及最大价值时各公司的选择数量。
分析与解决
通过题意分析,我们发现这显然是一个分组背包问题,所以对于第一问,我们可以直接套用分组背包的板子,令
f
[
i
]
[
j
]
f[i][j]
f[i][j] 的含义为前
i
i
i 家公司,选择代价不超过
j
j
j 的集合,枚举决策
k
k
k 表示在当前代价不超过
j
j
j 时的某个选择的代价,得出状态转移方程:
f
[
i
]
[
j
]
=
max
(
f
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
−
k
]
+
w
[
i
]
[
k
]
)
f[i][j]=\max(f[i-1][j],f[i-1][j-k] + w[i][k])
f[i][j]=max(f[i−1][j],f[i−1][j−k]+w[i][k])
考虑滚动数组优化为一维数组,最终得到状态转移方程为:
f
[
j
]
=
max
(
f
[
j
]
,
f
[
j
−
k
]
+
w
[
i
]
[
k
]
)
f[j]=\max(f[j],f[j-k]+w[i][k])
f[j]=max(f[j],f[j−k]+w[i][k])
对于第二问,考虑用
w
a
y
way
way 数组维护
max
(
{
f
[
j
]
}
)
\max(\left.\{f[j]\right.\})
max({f[j]})时的决策
k
k
k,随状态转移而更新。特别注意地,在进行状态转移时对于物品(机器)和代价的循环为倒序枚举,因为我们最后要求的是字典序升序输出,需要对大一点的先更新。
AC代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 100, M = 100;
int n, m;
int f[N];
int w[N][N];
int way[N][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 = n; i ; i--)
{
for (int j = m; j >= 0; j--)
{
for (int k = 1; k <= j; k++)
{
if (f[j] < f[j - k] + w[i][k])
{
f[j] = f[j - k] + w[i][k];
way[i][j] = k;
}
}
}
}
cout << f[m] << endl;
int j = m;
for (int i = 1; i <= n; i++)
{
cout << i << " " << way[i][j] << endl;
j -= way[i][j];
}
return 0;
}