题目链接
http://codeforces.com/contest/742/problem/D
思路
首先,用并查集将有朋友关系的人分为一组,然后剩下的就是分组背包的问题了
分组背包
设有k个分组,每个分组内有一些物品(每个物品价值
bi
,重量
wi
)
现在有一个背包,容量为v,要从这k组里面,每一组选一个或者一个都不选,求能选取的最大价值
状态表示 d[k][j] :当前选到第k组,背包容量为j
转移:第k - 1组里面一个都不选,或者选取了满足条件的一个
转移方程: d[k][j]=max{d[k][j],d[k−1][j−wi]+bi|对所有的物品i∈第k组}
伪代码:
for (int k = 1; k <= cnt; k++) {
for (int j = v; j >= 0; j--) {
for (int i = 1; i <= n; i++) {
if (i 属于第k组) {
if (j >= w[i]) d[k][j] = max(d[k - 1][j], d[k - 1][j - w[i]] + b[i]);
}
}
}
}
那么这道题,其实和分组背包基本一样,只是存在比如一个组内的物品全部选择的情况,有两种解决方案:
1. 每组内加入一个虚物品,重量是这组内物品的重量和,价值也是这组内的价值和,然后dp同前面的分组背包
2. dp状态转移的时候加一个转移,即上一组物品全部选择的情况
代码
#include <bits/stdc++.h>
using namespace std;
inline int in() {int x; scanf("%d", &x); return x;}
#define pr(x) {cout << #x << ' ' << x << endl;}
const int maxn = 1000 + 5;
int n, m, W;
int b[maxn], w[maxn];
int p[maxn];
int find(int x) {
return p[x] == x ? x : p[x] = find(p[x]);
}
void Union(int x, int y) {
x = find(x), y = find(y);
if (x != y) {
p[x] = y;
}
}
int cnt = 0;
int pab[maxn], paw[maxn];
const int maxm = 600000 + 5;
int f[maxm];
int vis[maxn];
int dp2() {
memset(f, 0, sizeof(f));
for (int k = 1; k <= cnt; k++) {
for (int j = W; j >= 0; j--) {
if (j >= paw[k]) f[j] = max(f[j], f[j - paw[k]] + pab[k]);
for (int i = 1; i <= n; i++) {
if (vis[p[i]] != k) continue;
if (j >= w[i]) f[j] = max(f[j], f[j - w[i]] + b[i]);
}
}
}
int res = 0;
for (int i = 0; i <= W; i++) res = max(res, f[i]);
return res;
}
map<int, int> mm;
int main() {
n = in();m = in(); W = in();
for (int i = 1; i <= n; i++) w[i] = in();
for (int i = 1; i <= n; i++) b[i] = in();
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 0; i < m; i++) {
int x = in(); int y = in();
Union(x, y);
}
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++) {
int x = find(i);
if (!vis[x]) {
vis[x] = ++cnt;
}
}
for (int i = 1; i <= n; i++) {
int x = find(i);
int ind = vis[x];
pab[ind] += b[i];
paw[ind] += w[i];
}
int res = 0;
res = max(res, dp2());
cout << res << endl;
return 0;
}