原题链接:https://www.luogu.com.cn/problem/P1064
难度:普及+/提高
涉及知识点:分组背包,有依赖的背包问题,动态规划
金明的预算方案一题是“有依赖的背包问题”的始祖,正因为有了这题才有了它。
如果读者是较早看到这篇博文的,那么我可能还没有放我的学习笔记,当然笔者会尽快地更新。
给定 m m m 组物品,每组物品包括主件和附件,选择了某个附件必须选择其所依附的主件,求在不超过给定的限制代价 m m m 下选择出来的最大价值。这就是极典型的有依赖的背包问题。
此题难在难在数据处理,考虑用
p
a
i
r
<
i
n
t
,
i
n
t
>
pair
然后我们思考如何解决问题。可以这样思考,因为选择了某组物品的某个附件那就必须选择它的主件,附件选择的个数是自由决定的,那么我们就可以用 1 个主件和若干个附件组合起来,搭配成一个物品,这样,每组物品中就存在可以直接选择的若干物品了,我们只需要选择其中一种搭配就行了。显而易见的,有依赖的背包问题就被我们转化成了分组背包问题。
这里有个问题,每组有多少个搭配呢?我们可以转化为树形结构来直观计算,以下图为例:
分类讨论一下:
既然转化为了分组背包问题,那么就要讨论一下处理好数据后的核心部分了。我们都知道分组背包问题分为枚举物品、代价、决策三部分。
k >> u & 1
。需要选择的附件就把这个它的代价和价值都累加到先前准备的主件的代价和价值上。在全部附件枚举完成后,再进行状态转移,状态转移的方程为#include
#include
#include
using namespace std;
typedef pair<int, int> PII;
#define v first
#define w second
const int N = 3e5;
int n, m;
PII master[N];
vector<PII> servant[N];
int f[N];
int main()
{
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
int v, w, q;
cin >> v >> w >> q;
if (!q) master[i] = {v, v * w};
else servant[q].push_back({v, v * w});
}
for (int i = 1; i <= n; i++) //枚举物品
{
if (master[i].v)
{
for (int j = m; j >= 0; j--)
{
auto &sv = servant[i];
for (int k = 0; k < 1 << sv.size(); k++)
{
int v = master[i].v, w = master[i].w;
for (int u = 0; u < sv.size(); u++)
{
if (k >> u & 1)
{
v += sv[u].v;
w += sv[u].w;
}
}
if (j >= v) f[j] = max(f[j], f[j - v] + w);
}
}
}
}
cout << f[m];
return 0;
}