原题链接
题意
思路
由题意易得,可以将 n 个人分成朋友圈,每个朋友圈只有两种选择,一是选择其中一个人,二是全选。
给 n 个人分组可以用并查集维护,然后用一个小技巧,用 vector 数组记录下每个人处于哪一个朋友圈。
分组后显然就可以分组背包,把每一个人作为物品,重量是物品的体积,魅力值是物品的价值。同时别忘了将朋友圈内所有人作为一个物品进行背包。
并查集
初始化
for (int i = 1; i <= n; i ++ )
p[i] = i;
查集
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
并集
p[find(x)] = find(y);
分组背包
v[i][j] 体积 w[i][j] 价值
for (int i = 1; i <= n; i ++ )
for (int j = m; j >= 0; j -- )
for (int k = 1; k <= s[i]; k ++ )
if (j >= v[i][k]) f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
完整Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
const int M = 1e6 + 10;
int n, m, w_max;
int f[M], p[N];
// 每个人的重量和魅力值
int w[N], b[N];
// 每个朋友圈所有人的重量和魅力值
int sum_w[N], sum_b[N];
// 记录每个朋友圈的人
vector<int> a[N];
// 查集
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
cin >> n >> m >> w_max;
for (int i = 1; i <= n; i ++ ) cin >> w[i];
for (int i = 1; i <= n; i ++ ) cin >> b[i];
for (int i = 1; i <= n; i ++ )
p[i] = i;
// 并集
for (int i = 1; i <= m; i ++ )
{
int x, y; cin >> x >> y;
p[find(x)] = find(y);
}
// 以代表元为编号记录每个组
for (int i = 1; i <= n; i ++ )
{
a[find(i)].push_back(i);
sum_w[find(i)] += w[i];
sum_b[find(i)] += b[i];
}
// 分组背包
for (int i = 1; i <= n; i ++ )
if (find(i) == i) // 是否为代表元
for (int j = w_max; j >= 0; j -- )
{
// 每个人
for (int k = 0; k < a[i].size(); k ++ )
if (j >= w[a[i][k]])
f[j] = max(f[j], f[j - w[a[i][k]]] + b[a[i][k]]);
// 所有人
if (j >= sum_w[i]) f[j] = max(f[j], f[j - sum_w[i]] + sum_b[i]);
}
cout << f[w_max] << endl;
return 0;
}