uva 1151 Buy or Build
题目大意:给出n个点以及m种套餐,每种套餐的数据包括改套餐所包含点的数量,该套餐的费用,以及改套餐所包含的具体的点的编号。点与点之间的费用是他们的欧几里得距离的平方。现在问,在使用套餐的情况下连接所有点的最小费用。套餐可以叠加。
解题思路:先求出不用套餐构造最小生成树的最小费用。然后开始枚举用或不用改套餐构造最小生成树的费用(二进制枚举子集),维护该费用,最小值就是答案。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int N = 1005;
const int M = N * N;
int n, p;
int X[N], Y[N];
int fa[N];
struct SubNet{
int num, cost;
int rec[N];
}sn[10];
struct Edge{
int u, v;
ll len;
}edges[M];
int en;
int cmp(Edge a, Edge b) {
return a.len < b.len;
}
ll getlen(int x, int y) {
return pow(X[x] - X[y], 2) + pow(Y[x] - Y[y], 2);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void init() {
en = 0;
for (int i = 0; i < N ;i++) fa[i] = i;
}
void input() {
for (int i = 0; i < p; i++) {
scanf("%d %d", &sn[i].num, &sn[i].cost);
for (int j = 0; j < sn[i].num; j++) {
scanf("%d", &sn[i].rec[j]);
}
}
for (int i = 1; i <= n; i++) {
scanf("%d %d", &X[i], &Y[i]);
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
edges[en++] = (Edge){i, j, getlen(i, j)};
}
}
sort(edges, edges + en, cmp);
}
ll kruskal() {
ll ans = 0;
for (int i = 0; i < en; i++) {
int x = find(edges[i].u), y = find(edges[i].v);
if (x != y) {
fa[x] = y;
ans += edges[i].len;
}
}
return ans;
}
void solve() {
ll ans = kruskal();
for (int B = 0; B < (1 << p); B++) {
ll temp = 0;
for (int i = 0; i <= n; i++) fa[i] = i;
for (int i = 0; i < p; i++) {
if (B & (1 << i)) {
temp += sn[i].cost;
for (int j = 1; j < sn[i].num; j++) {
fa[find(sn[i].rec[j - 1])] = find(sn[i].rec[j]);
}
}
}
temp += kruskal();
ans = min(ans, temp);
}
printf("%lld\n", ans);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &p);
init();
input();
solve();
if (T) puts("");
}
return 0;
}