UVa 1151 (枚举 + MST) Buy or Build

题意:

平面上有n个点,现在要把它们全部连通起来。现在有q个套餐,如果购买了第i个套餐,则这个套餐中的点全部连通起来。也可以自己单独地建一条边,费用为两点欧几里得距离的平方。求使所有点连通的最小费用。

分析:

很明显,如果没有这些套餐的话,就是一个裸的MST。

可以枚举套餐的组合情况,然后把套餐中的边的权值置为0,再求MST。

在求MST的过程中,并不需要把所有的边都加进来。只要把原图的MST的那些边和套餐中的边加进来即可。

因为,对于不在原图的MST的边,购买套餐以后,按照权值排序,排在它之前的边不会减少,反而会因为购买套餐而在前面多出来一些权值为0的边。所以原图中被“淘汰”的边,在购买套餐后也一定会被“淘汰”。

 1 #include <cstdio>

 2 #include <vector>

 3 #include <algorithm>

 4 using namespace std;

 5 

 6 const int maxn = 1000 + 10;

 7 const int maxq = 8;

 8 int n;

 9 int x[maxn], y[maxn], cost[maxq];

10 vector<int> subn[maxq];

11 

12 int pa[maxn];

13 int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); }

14 

15 struct Edge

16 {

17     int u, v, d;

18     Edge(int u, int v, int d):u(u), v(v), d(d) {}

19     bool operator < (const Edge& rhs) const { return d < rhs.d; }

20 };

21 

22 int MST(int cnt, vector<Edge>& e, vector<Edge>& used)

23 {

24     if(cnt == 1) return 0;

25     int m = e.size();

26     int ans = 0;

27     used.clear();

28     for(int i = 0; i < m; ++i)

29     {

30         int u = findset(e[i].u), v = findset(e[i].v);

31         if(u != v)

32         {

33             pa[u] = v;

34             ans += e[i].d;

35             used.push_back(e[i]);

36             if(--cnt == 1) break;

37         }

38     }

39     return ans;

40 }

41 

42 int main()

43 {

44     //freopen("in.txt", "r", stdin);

45     int T;

46     scanf("%d", &T);

47     while(T--)

48     {

49         int q;

50         scanf("%d%d", &n, &q);

51         for(int i = 0; i < q; ++i)

52         {

53             int cnt;

54             scanf("%d%d", &cnt, &cost[i]);

55             subn[i].clear();

56             while(cnt--)

57             {

58                 int u;

59                 scanf("%d", &u);

60                 subn[i].push_back(u-1); //代码中节点的编号是从0开始的

61             }

62         }

63         for(int i = 0; i < n; ++i) scanf("%d%d", &x[i], &y[i]);

64 

65         vector<Edge> e, need;

66         for(int i = 0; i < n; ++i)

67             for(int j = 0; j < i; ++j)

68             {

69                 int c = (x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]);

70                 e.push_back(Edge(i, j, c));

71             }

72 

73         for(int i = 0; i < n; ++i) pa[i] = i;

74         sort(e.begin(), e.end());

75 

76         int ans = MST(n, e, need);

77         for(int S = 0; S < (1<<q); ++S)

78         {//枚举所有套餐的组合情况

79             for(int i = 0; i < n; ++i) pa[i] = i;

80             int cnt = n, c = 0;

81             for(int i = 0; i < q; ++i) if(S & (1<<i))

82             {

83                 c += cost[i];

84                 for(int j = 1; j < subn[i].size(); ++j)

85                 {

86                     int u = findset(subn[i][j]), v = findset(subn[i][0]);

87                     if(u != v) { --cnt; pa[u] = v; }

88                 }

89             }

90             vector<Edge> hehe;  //这里只是为了调用函数,所以弄了一个无关紧要的参数

91             ans = min(ans, c + MST(cnt, need, hehe));

92         }

93 

94         printf("%d\n", ans);

95         if(T) printf("\n");

96     }

97 

98     return 0;

99 }
代码君

 

你可能感兴趣的:(Build)