题目地址:
https://www.acwing.com/problem/content/1254/
Joe觉得云朵很美,决定去山上的商店买一些云朵。商店里有 n n n朵云,云朵被编号为 1 , 2 , … , n 1,2,…,n 1,2,…,n,并且每朵云都有一个价值。但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。但是Joe的钱有限,所以他希望买的价值越多越好。
输入格式:
第
1
1
1行包含三个整数
n
n
n,
m
m
m,
w
w
w,表示有
n
n
n朵云,
m
m
m个搭配,Joe有
w
w
w的钱。第
2
∼
n
+
1
2∼n+1
2∼n+1行,每行两个整数
c
i
c_i
ci,
d
i
d_i
di表示
i
i
i朵云的价钱和价值。第
n
+
2
∼
n
+
1
+
m
n+2∼n+1+m
n+2∼n+1+m行,每行两个整数
u
i
u_i
ui,
v
i
v_i
vi,表示买
u
i
u_i
ui就必须买
v
i
v_i
vi,同理,如果买
v
i
v_i
vi就必须买
u
i
u_i
ui。
输出格式:
一行,表示可以获得的最大价值。
数据范围:
1
≤
n
≤
10000
1≤n≤10000
1≤n≤10000
0
≤
m
≤
5000
0≤m≤5000
0≤m≤5000
1
≤
w
≤
10000
1≤w≤10000
1≤w≤10000
1
≤
c
i
≤
5000
1≤c_i≤5000
1≤ci≤5000
1
≤
d
i
≤
100
1≤d_i≤100
1≤di≤100
1
≤
u
i
,
v
i
≤
n
1≤u_i,v_i≤n
1≤ui,vi≤n
由于某个组合必须一起买,所以想到将每个组合捏成一个物品,算出其价格和价值,然后用 0 − 1 0-1 0−1背包问题的求解方式求解。捏成一个物品的过程可以用并查集来做,在合并的时候,同时要记录每个集合的总价格和总价值。并且在做动态规划的时候,只考虑根节点的信息(根节点存的就是其所在集合的总价格和总价值)。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10010;
int n, m, vol;
int v[N], w[N];
int p[N];
int f[N];
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main() {
scanf("%d%d%d", &n, &m, &vol);
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 1; i <= n; i++) scanf("%d%d", &v[i], &w[i]);
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
int pa = find(a), pb = find(b);
if (pa != pb) {
p[pa] = pb;
v[pb] += v[pa];
w[pb] += w[pa];
}
}
for (int i = 1; i <= n; i++)
if (p[i] == i)
// 0-1背包里,价格要从大到小遍历
for (int j = vol; j >= v[i]; j--)
f[j] = max(f[j], f[j - v[i]] + w[i]);
printf("%d\n", f[vol]);
return 0;
}
时间复杂度 O ( n + m log ∗ n + n w ) O(n+m\log^*n+nw) O(n+mlog∗n+nw),空间 O ( n ) O(n) O(n)。