题目大意:平面上有n个点,你的任务是让n个点连通。你可以新建一些边,费用等于两个端点的欧几里得距离。另外还有q个“套餐”可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得连通。
求让n个点连通的最小花费。
解析:具体见《算法竞赛入门经典(第二版)》 358页
主要算法:Kruskal、枚举、位运算
位运算
//位运算枚举子集
for (int i = 0; i < (1 << q); i++) {
for (int j = 0; j < q; j++)
cout << (i & (1 << j)) << ' ';
cout << endl;
}
ac代码
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 1005;
const int MAXM = 500005;
int f[MAXN],x[MAXN],y[MAXN];
int n, m, t, tol;
struct Edge {
int u, v, c;
bool operator <(const Edge& r)const {
return c < r.c;
}
};
struct T {
int c, p;
vector<int> v;
}tc[8];
vector<Edge> ve, vve;
int find(int x) {
return f[x] == -1 ? x : f[x] = find(f[x]);
}
int kruskal1(int n) {
int ans = 0, cnt = 0;
int l = vve.size();
for (int i = 0; i < l; i++) {
int u = vve[i].u, v = vve[i].v, c = vve[i].c;
int fx = find(u), fy = find(v);
if (fx != fy) {
f[fx] = fy;
ve.push_back(Edge{ u, v, c });
ans += c;
cnt++;
}
if (cnt == n - 1) {
break;
}
}
return ans;
}
int kruskal2(int n, const vector<Edge>& vv) {
int ans = 0, cnt = 0;
for (int i = 0; i < vv.size(); i++) {
int u = vv[i].u, v = vv[i].v, c = vv[i].c;
int fx = find(u), fy = find(v);
if (fx != fy) {
f[fx] = fy;
ans += c;
cnt++;
}
if (cnt == n - 1) {
break;
}
}
return ans;
}
int main() {
IOS;
cin >> t;
while (t--) {
vve.clear();
ve.clear();
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> tc[i].c >> tc[i].p;
tc[i].v.clear();
for (int j = 0; j < tc[i].c; j++) {
int num;
cin >> num;
tc[i].v.push_back(num);
}
}
for (int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
for (int i = 1; i < n; i++)
for (int j = i + 1; j <= n; j++) {
int c = ((x[i]- x[j]) * (x[i]- x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
vve.push_back(Edge{ i,j,c });
}
sort(vve.begin(), vve.end());
memset(f, -1, sizeof(f));
int minc = kruskal1(n);
for (int i = 0; i < (1 << m); i++) {
memset(f, -1, sizeof(f));
int minn = 0, b = n;
for (int j = 0; j < m; j++)
if (i & (1 << j)) {
minn += tc[j].p;
for (int k = 1; k < tc[j].v.size(); k++) {
int u = tc[j].v[0], v = tc[j].v[k];
int fx = find(u), fy = find(v);
if (fx != fy) {
f[fx] = fy;
b--;
}
}
}
minc = min(minc, minn + kruskal2(b, ve));
}
cout << minc << endl;
if (t)
cout << endl;
}
return 0;
}