UVa 1151 Buy or Build

来源:互联网 发布:蜜桃tv源码 编辑:程序博客网 时间:2024/05/17 10:56


来源:《算法竞赛入门经典》(第二版)例题11-3、UVa 1151
题目描述:
给定n个结点和q个子网络,每个子网络有若干结点,如果要关联,则需花费一定费用.现在要让n个结点互相关联,除了通过子网络外,还可直接连通路,耗费是两者距离的平方.求达到目标的最小花费.
题目分析:
子网络数目上限只有8个,可以生成选取子集来枚举.当n个结点给定后,这个图是一个完全图,可以预先通过Kruskal得到什么网络都不选的最小生成树,注意到这时被排除的边以后也不可能再用.接下来对每种可能的子网路选法,将子网络中边加入之前由最小生成树的边集中,再做一次最小生成树求解.

使用vector的版本(70ms)

//  Created by wander on 16/6/2.//  Copyright © 2016年 W4anD0eR96. All rights reserved.//  UVa 1151 Buy or Build//  类型:图论-生成树#include "bits/stdc++.h"using namespace std;int kase, n, m, p[1050], res;struct Edge { int u, v, w; };bool f(const Edge& a, const Edge& b) { return a.w < b.w; }vector<Edge> e;pair<int, int> pos[1050];struct SubNet { int c, n, no[1050]; }sub[15];int Dist(pair<int, int> a, pair<int, int> b) {  return (a.first - b.first) * (a.first - b.first)  + (a.second - b.second) * (a.second - b.second);}int Find(int x) { return (p[x] != x) ? (p[x] = Find(p[x])) : x; }int main(){#ifdef DEBUG  freopen("in", "r", stdin);  freopen("out", "w", stdout);#endif  scanf("%d", &kase);  while (kase--) {    e.clear();    scanf("%d%d",&n,&m);    for (int i = 0 ; i < m ; i += 1){      scanf("%d%d",&sub[i].n, &sub[i].c);      for(int j = 0 ; j < sub[i].n ; j  += 1)        scanf("%d", &sub[i].no[j]);    }    for(int i = 1 ; i <= n ; i += 1) {      scanf("%d%d", &pos[i].first, &pos[i].second);      for(int j = 1 ; j < i; j += 1)        e.push_back((Edge){j, i, Dist(pos[i], pos[j])});    }    sort(e.begin(), e.end(), f);    res = 0; for(int i = 0 ; i <= n ; i += 1) p[i] = i;    for (int i = 0, cnt = 0; cnt < n - 1; i += 1) {      int x = Find(e[i].u), y = Find(e[i].v);      if (x != y) { res += e[i].w; p[x] = y; e[cnt] = e[i]; cnt += 1; }    }    e.resize(n - 1);    for (int state = 1; state < (1 << q) ; state += 1) {      int cnt = 0, t = 0;      for (int i = 0 ; i <= n; i += 1) p[i] = i;      for (int i = 0 ; i < q; i += 1) if ((state >> i) & 1) {        t += sub[i].c;        for (int k = 1 ; k < sub[i].n ; k += 1) {          int x = Find(sub[i].no[k]), y = Find(sub[i].no[k - 1]);          if (x != y) { p[x] = y; cnt += 1; }        }      }      for (int i = 0; cnt < n - 1; i += 1) {        int x = Find(e[i].u), y = Find(e[i].v);        if (x != y) { t += e[i].w; p[x] = y; cnt += 1; }      }      res = min(res, t);    }    printf("%d\n",res);    if(kase > 0)      printf("\n");  }  return 0;}

几乎全手写的代码,然而速度似乎更慢一些(120ms)

//  Created by wander on 16/6/2.//  Copyright © 2016年 W4anD0eR96. All rights reserved.//  UVa 1151 Buy or Build//  类型:图论-生成树#include "bits/stdc++.h"using namespace std;int kase, n, q, es, res, p[1050], id[1050];int u[500050], v[500050], w[500050], r[500050];bool f(int i, int j) { return w[i] < w[j]; }pair<int, int> pos[1050];struct SubNet { int c, n, no[1050]; }sub[15];int Dist(pair<int, int>& a, pair&lt;int, int>& b) {  return (a.first - b.first) * (a.first - b.first)  + (a.second - b.second) * (a.second - b.second);}int Find(int x) { return (p[x] != x) ? (p[x] = Find(p[x])) : x; }int main(){#ifdef DEBUG  freopen("in", "r", stdin);  freopen("out", "w", stdout);#endif  scanf("%d", &kase);  while (kase--) {    scanf("%d%d", &n, &q);    for (int i = 0 ; i < q ; i += 1){      scanf("%d%d",&sub[i].n, &sub[i].c);      for (int j = 0 ; j < sub[i].n ; j += 1)        scanf("%d", &sub[i].no[j]);    }    es = 0;    for (int i = 1 ; i <= n ; i += 1) {      scanf("%d%d",&pos[i].first, &pos[i].second);      for (int j = 1 ; j < i ; j += 1) {        u[es] = j; v[es] = i; w[es] = Dist(pos[i], pos[j]); es += 1;      }    }    res = 0;    for (int i = 0; i <= n; i += 1) p[i] = i;    for (int i = 0; i < es; i += 1) r[i] = i;    sort(r, r + es, f);    for (int i = 0, cnt = 0; cnt < n - 1; i += 1) {      int e = r[i], x = Find(u[e]), y = Find(v[e]);      if (x != y) { res += w[e]; p[x] = y; id[cnt] = e; cnt += 1; }    }    for (int state = 1; state < (1 << q) ; state += 1) {      int cnt = 0, t = 0;      for (int i = 0 ; i <= n; i += 1) p[i] = i;      for (int i = 0 ; i < q; i += 1) if ((state >> i) & 1) {        t += sub[i].c;        for (int k = 1 ; k < sub[i].n ; k += 1) {          int x = Find(sub[i].no[k]), y = Find(sub[i].no[k - 1]);          if (x != y) { p[x] = y; cnt += 1; }        }      }      for (int i = 0; cnt < n - 1; i += 1) {        int x = Find(u[id[i]]), y = Find(v[id[i]]);        if (x != y) { t += w[id[i]]; p[x] = y; cnt += 1; }      }      res = min(res, t);    }    printf("%d\n",res);    if (kase) putchar('\n');  }  return 0;}
0 0
原创粉丝点击