[题解] uva 1151 Buy or Build (kruskal最小生成树)

- 传送门 -

 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3592

# 1151 - Buy or Build Time limit: 3.000 seconds | [Root](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=0) | [![Submit](https://uva.onlinejudge.org/components/com_onlinejudge/images/button_submit.png "Submit")](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=submit_problem&problemid=3592&category=) [![Problem Stats](https://uva.onlinejudge.org/components/com_onlinejudge/images/button_stats.png "Problem Stats")](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=problem_stats&problemid=3592&category=) [![uDebug](https://uva.onlinejudge.org/components/com_onlinejudge/images/button_debug.png)](https://www.udebug.com/UVa/1151) [![Download as PDF](https://uva.onlinejudge.org/components/com_onlinejudge/images/button_pdf.png "Download as PDF")](https://uva.onlinejudge.org/external/11/1151.pdf) | ###(题面见pdf)
  ### - 题意 -  有n个节点(给出坐标), 在两节点间建边的代价为它们距离的平方, 同时有q个套餐,每个套餐有一个价格和若干个点. 付出套餐价即可使套餐中所有点联通(买套餐的数量没有限制).  求出使所有点联通的最小代价.   ### - 思路 -  考虑套餐数量很小( <= 8 ), 可以用二进制枚举, 枚举 $1$ 到 $2^q-1$, 若第k位上是1, 则取第 k 种套餐.  把取到的套餐中的点都联通之后, 再进行一次kruskal求出总代价.    细节见代码.    PS:  因为要二进制枚举, 套餐我是从0开始标记的, 但手残把点也改成从 0 开始标记了(因为记录套餐时是默认节点从1开始的), 然后调啊调...   ### - 代码 - ```c++ #include #include #include #include using namespace std;

const int N = 1000 + 5;
const int inf = 0x7fffffff;

struct edge {
int x, y, v;
}W[N*N];

int F[N], X[N], Y[N], C[10];
int cnt, cst, ans, n, m, T, sz;

vector S[10];

bool cmp (edge a, edge b) { return a.v < b.v; }

int distance(int a, int b) {
return (X[a] - X[b]) * (X[a] - X[b]) + (Y[a] - Y[b]) * (Y[a] - Y[b]);
}

int find(int x) { return F[x] == x ? x : F[x] = find(F[x]); }

int merge(int a, int b) {
int xf = find(a), yf = find(b);
if (xf == yf) return 0;
F[xf] = yf;
cnt ++;
return 1;
}

int kruskal() {
int tot = 0;
for (int i = 1, tmp; i <= sz; ++i) {
tmp = merge(W[i].x, W[i].y);
if (tmp) tot += W[i].v;
if (cnt == n - 1) break; //注意cnt的初始化
}
return tot;
}

void init() {
cnt = cst = 0;
for (int i = 0; i <= n; ++i)
F[i] = i;
}

int main() {
scanf("%d", &T);
for (int cas = 0; cas < T; ++cas) {
if (cas) printf("\n"); //uva诡异的输出
sz = cnt = 0;
scanf("%d%d", &n, &m);
for (int i = 0, ct, tmp; i < m; ++i) {
scanf("%d%d", &ct, &C[i]);
S[i].clear();
for (int j = 1; j <= ct; ++j) {
scanf("%d", &tmp);
S[i].push_back(tmp);
}
}
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &X[i], &Y[i]);
for (int j = 1; j < i; ++j) {
W[++sz].x = j; W[sz].y = i;
W[sz].v = distance(i, j);
}
}
sort(W + 1, W + sz + 1, cmp);
init();
ans = kruskal();//不买套餐的情况
for (int i = 1; i < (1 << m); ++i) {
init();//初始化
for (int j = 0; j < m; ++j) {
if (i & (1 << j)) {
for (int k = 1, tmp; k < S[j].size(); ++k)
tmp = merge(S[j][k], S[j][0]);
cst += C[j];
}
}
int aa = kruskal();
ans = min(ans, cst + aa);
}
printf("%d\n", ans);
}
}

posted @ 2017-08-13 20:58  lstttt  阅读(75)  评论(0编辑  收藏  举报