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

链接:

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

题意:

平面上有n个点(1≤n≤1000),你的任务是让所有n个点连通。
为此,你可以新建一些边,费用等于两个端点的欧几里德距离的平方。
另外还有q(0≤q≤8)个“套餐”可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通。
第i个套餐的花费为Ci。求最小的花费。

分析:

最容易想到的算法是:先枚举购买哪些套餐,把套餐中包含的边的权值设为0,然后求最小生成树。
由于枚举量为O(2^q),给边排序的时间复杂度为O(n*nlogn),而排序之后每次Kruskal算法的时间复杂度为O(n*n),
因此总时间复杂度为O((2^q)*(n*n)+n*nlogn),对于题目的规模来说太大了。
只需一个小小的优化即可降低时间复杂度:先求一次原图(不购买任何套餐)的最小生成树,
得到n-1条边,然后每次枚举完套餐后只考虑套餐中的边和这n-1条边,
则枚举套餐之后再求最小生成树时,图上的边已经寥寥无几。
为什么可以这样呢?首先回顾一下,在Kruskal算法中,哪些边不会进入最小生成树。
答案是:两端已经属于同一个连通分量的边。买了套餐以后,相当于一些边的权变为0,
而对于不在套餐中的每条边e,排序在e之前的边一个都没少,反而可能多了一些权值为0的边,
所以在原图Kruskal时被“扔掉”的边,在后面的Kruskal中也一样会被扔掉。

代码:

 1 import java.io.*;
 2 import java.util.*;
 3 import static java.lang.Math.*;
 4
 5 public class Main {
 6     Scanner cin = new Scanner(new BufferedInputStream(System.in));
 7     final int UP = 1000 + 5;
 8     int pre[] = new int[UP];
 9     int x[] = new int[UP], y[] = new int[UP], cost[] = new int[UP];
10
11     class Edge implements Comparable<Edge> {
12         int f, b, v;
13
14         @Override
15         public int compareTo(Edge that) {
16             return v - that.v;
17         }
18     }
19
20     int seek(int v) {
21         return pre[v] == v ? v : (pre[v] = seek(pre[v]));
22     }
23
24     int MST(int n, ArrayList<Edge> e, ArrayList<Edge> res) {
25         if(n <= 1) return 0;
26         int m = e.size(), ans = 0;
27         for(int i = 0; i < m; i++) {
28             int pf = seek(e.get(i).f), pb = seek(e.get(i).b);
29             if(pf == pb) continue;
30             pre[pf] = pre[pb];
31             ans += e.get(i).v;
32             if(res != null) res.add(e.get(i));
33             if(--n == 1) break;
34         }
35         return ans;
36     }
37
38     void MAIN() {
39         int T;
40         T = cin.nextInt();
41         while(T --> 0) {
42             @SuppressWarnings("unchecked")
43             ArrayList<Integer> subn[] = new ArrayList[8];
44             for(int i = 0; i < 8; i++) subn[i] = new ArrayList<Integer>();
45             int n = cin.nextInt();
46             int q = cin.nextInt();
47             for(int m, i = 0; i < q; i++) {
48                 m = cin.nextInt();
49                 cost[i] = cin.nextInt();
50                 for(int t = 0; t < m; t++) subn[i].add(cin.nextInt()-1);
51             }
52             for(int i = 0; i < n; i++) {
53                 x[i] = cin.nextInt();
54                 y[i] = cin.nextInt();
55             }
56
57             ArrayList<Edge> edge = new ArrayList<Edge>();
58             for(int i = 0; i < n; i++) {
59                 for(int t = i+1; t < n; t++) {
60                     Edge e = new Edge();
61                     e.f = i;  e.b = t;
62                     e.v = (x[i]-x[t])*(x[i]-x[t]) + (y[i]-y[t])*(y[i]-y[t]);
63                     edge.add(e);
64                 }
65             }
66
67             ArrayList<Edge> used = new ArrayList<Edge>();
68             for(int i = 0; i < n; i++) pre[i] = i;
69             Collections.sort(edge);
70             int ans = MST(n, edge, used);
71             for(int s = 1; s < (1<<q); s++) {
72                 for(int i = 0; i < n; i++) pre[i] = i; // 初始化并查集
73                 int remain = n, c = 0;
74                 for(int i = 0; i < q; i++) if((s&(1<<i)) > 0) {
75                     c += cost[i];
76                     for(int t = 1; t < subn[i].size(); t++) {
77                         int pf = seek(subn[i].get(0)), pb = seek(subn[i].get(t));
78                         if(pf == pb) continue;
79                         pre[pf] = pb;
80                         remain--;
81                     }
82                 }
83                 ans = min(ans, c + MST(remain, used, null));
84             }
85             System.out.println(ans);
86             if(T > 0) System.out.println();
87         }
88     }
89
90     public static void main(String args[]) { new Main().MAIN(); }
91 }

原文地址:https://www.cnblogs.com/hkxy125/p/9527568.html

时间: 2024-07-28 19:04:39

UVa 1151 - Buy or Build(最小生成树)的相关文章

UVa 1151 Buy or Build (最小生成树+二进制法暴力求解)

题意:给定n个点,你的任务是让它们都连通.你可以新建一些边,费用等于两点距离的平方(当然越小越好),另外还有几种“套餐”,可以购买,你购买的话,那么有些边就可以连接起来, 每个“套餐”,也是要花费的,让你求出最少花费. 析:首先想到的是把所有情况都考虑算一下,然后找出最少的,先算没有“套餐”的,然后算有的,用二进制枚举的话,总时间复杂度为O(2qn2+n2logn),这个时间复杂度太大了吧,肯定会超时, 那么我们就可以优化一下,首先先算出来最小生成树,并且把每条边都保存下来,那么加了“套餐”之后

UVa 1151 Buy or Build【最小生成树】

题意:给出n个点的坐标,现在需要让这n个点连通,可以直接在点与点之间连边,花费为两点之间欧几里得距离的平方,也可以选购套餐,套餐中所含的点是相互连通的 问最少的花费 首先想kruskal算法中,被加入的边已经是最优的了,所以当选择完套餐后,之前被丢弃的边也不会再进入最小生成树 然后就可以先求一次原图的最小生成树,保存下进入最小生成树的n-1条边 再枚举选择的套餐的情况,再求最小生成树,这里用的二进制法枚举 最后维护一个最小值就可以了 思路虽然看懂了,可是代码根本就写不出来,看着标程写的,最后还是

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

也是简单的最小生成树算法 不过添加了一些新的东西,需要对最小生成树算法 以及其中的 并查集的使用 有一些比较深入的理解. 处理问题的方法也有些复杂 #include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int maxn = 1005; struct point { int x; int y; }pp[maxn]; s

UVA 1151 Buy or Build (MST最小生成树,kruscal,变形)

题意:要使n个点之间能够互通,要使两点直接互通需要耗费它们之间的欧几里得距离的平方的权(注意不需要开方的),这说明每两个点都可以使其互通.接着又q个套餐可以选,一旦选了这些套餐,他们所包含的点自动就连起来了,所需要做的就是连上还未通的即可,q<=8.可以多买.求最小生成树所需的代价. 思路:与普通求MST不同的就是多了套餐,而且还可以多买.每个套餐有买或不买两种可能,那么有28种可能,即256种. 如果不买套餐,至少需要求1次MST是确定的,这个复杂度已经是O(n*n)了.还得考虑哪些餐套可以搭

UVA 1151 Buy or Build

先求出原图的最小生成树,然后枚举买哪些套餐,把一个套餐内的点相当与边权为0,直接用并查集缩点.正确性是基于一个贪心, 在Kruskal中,对于没有进入最小生成树的边,排序在它前面的边不会减少. 边比较多,用prim求最小生成树,效果比Kruskal好,枚举套餐的时候在用Kruskal. #include<bits/stdc++.h> using namespace std; const int maxn = 1005; int n,q; int C[9]; vector<int>

UVA 1151 Buy or build(并查集之六)

题意:n个结点,以及m个组合,可以任意选择.求最小花费. 因为组合最多只有8个,可以枚举. #include<stdio.h> #include<math.h> #include<algorithm> #include<string.h> #define MAX 1007 #define INTMAX (int)1e9 using namespace std; struct list_buy { int num; int price; int node[MA

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

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

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

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