Uva 1151 - Buy or Build (最小生成树+并查集)
题目链接 https://vjudge.net/problem/UVA-1151
【题意】
平面上有n个点(n <= 1000),你的任务是让这n个点联通,为此可以新建一些边,费用等于两个欧几里得距离的平方。另外还有q(0<=q<=8)个套餐可以购买,如果你购买了第i个套餐,该套餐中的所有结点变得相互联通。
【思路】
现求出原图的最小生成树,然后将最小生成树的边保存到另一个边集中,再依次枚举每种套餐的选择情况,如果选择了第i个套餐,那么就把第i个套餐中的点全部用并查集合并起来,最后再用kruskal算法求一次最小生成树,取总费用的最小值。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1050;
const int maxm = 500050;
int n, m, q;
int x[maxn], y[maxn], par[maxn];
int num[8], cost[8], node[8][maxn];
struct Edge {
int from, to, dist;
Edge(int f = 0, int t = 0, int d = 0) :from(f), to(t), dist(d) {}
}edges[maxm], mst[maxn];
int dis(int x1, int x2, int y1, int y2) { return (x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2); }
bool cmp(Edge a, Edge b) { return a.dist < b.dist; }
int find(int a) { return par[a] == a ? a : par[a] = find(par[a]); }
int kruskal() {
int ans = 0;
for (int i = 0; i < n - 1; ++i) {
int a = find(mst[i].from);
int b = find(mst[i].to);
if (a != b) {
par[a] = b;
ans += mst[i].dist;
}
}
return ans;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &q);
for (int i = 0; i < q; ++i) {
scanf("%d%d", &num[i], &cost[i]);
for (int j = 0; j < num[i]; ++j) {
scanf("%d", &node[i][j]);
--node[i][j];
}
}
for (int i = 0; i < n; ++i) { scanf("%d%d", &x[i], &y[i]); }
m = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
int dist = dis(x[i], x[j], y[i], y[j]);
edges[m++] = Edge(i, j, dist);
}
}
sort(edges, edges + m, cmp);
int cnt = 0, ans = 0;
for (int i = 0; i < n; ++i) par[i] = i;
for (int i = 0; cnt < n - 1; ++i) {
Edge e = edges[i];
int a = find(e.from);
int b = find(e.to);
if (a != b) {
par[a] = b;
mst[cnt++] = e;
ans += e.dist;
}
}
for (int s = 0; s < (1 << q); ++s) {
int tmp = 0;
for (int i = 0; i < n; ++i) par[i] = i;
for (int i = 0; i < q; ++i) {
if ((s >> i) & 1) {
tmp += cost[q - 1 - i];
for (int j = 0; j < num[q - 1 - i] - 1; ++j) {//选中套餐后在并查集中把套餐中的点合并
par[find(node[q - 1 - i][j])] = find(node[q - 1 - i][j + 1]);
}
}
}
ans = min(ans, tmp + kruskal());
}
printf("%d\n", ans);
if (t) putchar('\n');
}
return 0;
}