UVa1151 Buy or Build (最小生成树,枚举子集)

链接:http://bak.vjudge.net/problem/UVA-1151

分析:先在原图上跑一遍MST,得到n-1条边,然后其它的边完全可以抛弃掉,因为它们不会比这n-1条边更优,这样就可以把原图边的数量减少到n-1条,并且得到ans初值。

接下来就是通过枚举套餐子集,生成一个套餐费用c1并且得到若干联通分量,再在这些联通分量基础上跑MST得到最小花费c2,更新答案ans。

 1 #include <cstdio>
 2 #include <vector>
 3 #include <algorithm>
 4 using namespace std;
 5
 6 const int maxn = 1e3 + 5;
 7 const int maxq = 8;
 8
 9 int n, q, cost[maxn], x[maxn], y[maxn];
10 vector<int> subn[maxq];
11
12 int pa[maxn];
13 int findset(int x) { return pa[x] != x ? pa[x] = findset(pa[x]) : x; }
14
15 struct Edge {
16     int u, v, w;
17     Edge(int u, int v, int w): u(u), v(v), w(w) {}
18     bool operator < (const Edge& rhs) const {
19         return w < rhs.w;
20     }
21 };
22
23 int MST(int cnt, const vector<Edge>& e, vector<Edge>& used) {
24     if (cnt == 1) return 0;
25     int ans = 0;
26     int sz = e.size();
27     for (int i = 0; i < sz; i++) {
28         int u = findset(e[i].u); int v = findset(e[i].v);
29         if (u != v) {
30             pa[u] = v;
31             ans += e[i].w;
32             used.push_back(e[i]);
33             if (--cnt == 1) break;
34         }
35     }
36     return ans;
37 }
38
39 int main() {
40     int T;
41     scanf("%d", &T);
42     while (T--) {
43         scanf("%d%d", &n, &q);
44         for (int i = 0, cnt, u; i < q; i++) {
45             scanf("%d%d", &cnt, &cost[i]);
46             subn[i].clear();
47             while (cnt--) {
48                 scanf("%d", &u);
49                 subn[i].push_back(u - 1);
50             }
51         }
52         for (int i = 0; i < n; i++) scanf("%d%d", &x[i], &y[i]);
53         vector<Edge> e, need;
54         for (int i = 0; i < n; i++)
55             for (int j = i + 1; j < n; j++) {
56                 int d = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
57                 e.push_back(Edge(i, j, d));
58             }
59         for (int i = 0; i < n; i++) pa[i] = i;
60         sort(e.begin(), e.end());
61         int ans = MST(n, e, need);
62         for (int mask = 0; mask < (1 << q); mask++) {
63             for (int i = 0; i < n; i++) pa[i] = i;
64             int cnt = n, c = 0;
65             for (int i = 0; i < q; i++) if (mask & (1 << i)) {
66                 c += cost[i];
67                 for (int j = 1; j < subn[i].size(); j++) {
68                     int u = findset(subn[i][j]), v = findset(subn[i][0]);
69                     if (u != v) {
70                         pa[u] = v;
71                         cnt--;
72                     }
73                 }
74             }
75             vector<Edge> dummy;
76             ans = min(ans, c + MST(cnt, need, dummy));
77         }
78         printf("%d\n", ans);
79         if (T) putchar(‘\n‘);
80     }
81     return 0;
82 }
时间: 2024-10-10 10:19:07

UVa1151 Buy or Build (最小生成树,枚举子集)的相关文章

POJ 2784 Buy or Build最小生成树

Buy or Build Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1360   Accepted: 539 Description World Wide Networks (WWN) is a leading company that operates large telecommunication networks. WWN would like to setup a new network in Borduri

【最小生成树+子集枚举】Uva1151 Buy or Build

Description 平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方. 另外还有q(0<=q<=8)个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci. 求最小花费. Solution 对于套餐可以用子集枚举处理,求最小生成树时只需考虑原图是最小生成树中的边. 正确性可以按Kruskal过程,以前被舍弃的边选了套餐后依然会被舍弃. Code 1 #in

1151 - Buy or Build(二进制枚举子集 + 并查集)

这题LRJ书上翻译的有问题,书上说两点之间的cost是两点的欧几里得距离,而题目要求两点的距离是两点欧几里得距离的平方. 其余就没什么好说的了,裸的并查集,需要注意的就是二进制枚举子集的问题. 二进制枚举子集: for(int i = 0 ; i < (1 << s) ; i++){ /*s是集合元素的个数*/ for(int j = 0 ; j < s ; j++){ if(!(s >> j) & 1) continue; else{ } } } 140548

1151 - Buy or Build (最小生成树)

一道很好的最小生成树题目 . 看似非常复杂,其实仔细分析一下算法的复杂度就会发现,如果加入了lrj说的优化,其实复杂度不高 . 就像紫书中说的, 除去购买套餐中的点,剩下的最小边仍然在原始的最小生成树中 .  所以我们用二进制枚举子集的方法枚举所有购买套餐的组合,然后将套餐中的点加入并查集中,再用原始最小生成树中的边补全当前生成树 . 二进制枚举子集的复杂度是2^8 . 补全生成树的复杂度是O(n) . 所以最后复杂度为O(n*(2^8)) ,约等于10^6 ,可以接受. 有一个地方我不知道是不

Buy or Build (poj 2784 最小生成树)

Buy or Build Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1348   Accepted: 533 Description World Wide Networks (WWN) is a leading company that operates large telecommunication networks. WWN would like to setup a new network in Borduri

11825 - Hackers&#39; Crackdown 状态压缩 dp 枚举子集

11825 - Hackers' Crackdown 状态压缩 dp 枚举子集 ACM 题目地址:11825 - Hackers' Crackdown 题意: 有一个由编号0~n-1的n台计算机组成的网络,一共有n种服务,每台计算机上都运行着全部服务,对于每台计算机,你可以选择停止一项服务,这个行为会导致与这台计算机和与他相连的其他计算机上的这项服务都停止(原来已经停止的继续保持停止状态).求最多能使多少个服务瘫痪(即没有任何一台计算机在运行这项服务). 分析: 题目说白了,就是: 把n个集合p

UVA 11825 - Hackers&amp;#39; Crackdown 状态压缩 dp 枚举子集

UVA 11825 - Hackers' Crackdown 状态压缩 dp 枚举子集 ACM 题目地址:11825 - Hackers' Crackdown 题意: 有一个由编号0~n-1的n台计算机组成的网络,一共同拥有n种服务,每台计算机上都执行着所有服务,对于每台计算机,你能够选择停止一项服务,这个行为会导致与这台计算机和与他相连的其它计算机上的这项服务都停止(原来已经停止的继续保持停止状态). 求最多能使多少个服务瘫痪(即没有不论什么一台计算机在执行这项服务). 分析: 题目说白了.就

UVa 11025 The broken pedometer【枚举子集】

题意:给出一个矩阵,这个矩阵由n个数的二进制表示,p表示用p位二进制来表示的一个数 问最少用多少列就能将这n个数区分开 枚举子集,然后统计每一种子集用了多少列,维护一个最小值 b[i]==1代表的是选择了这一列 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector&g

HDU Untitled(状压DP OR dfs枚举子集)

Untitled Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 325    Accepted Submission(s): 169 Problem Description There is an integer a and n integers b1,-,bn. After selecting some numbers from b