链接:

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中也一样会被扔掉。

代码:

 import java.io.*;
import java.util.*;
import static java.lang.Math.*; public class Main {
Scanner cin = new Scanner(new BufferedInputStream(System.in));
final int UP = 1000 + 5;
int pre[] = new int[UP];
int x[] = new int[UP], y[] = new int[UP], cost[] = new int[UP]; class Edge implements Comparable<Edge> {
int f, b, v; @Override
public int compareTo(Edge that) {
return v - that.v;
}
} int seek(int v) {
return pre[v] == v ? v : (pre[v] = seek(pre[v]));
} int MST(int n, ArrayList<Edge> e, ArrayList<Edge> res) {
if(n <= 1) return 0;
int m = e.size(), ans = 0;
for(int i = 0; i < m; i++) {
int pf = seek(e.get(i).f), pb = seek(e.get(i).b);
if(pf == pb) continue;
pre[pf] = pre[pb];
ans += e.get(i).v;
if(res != null) res.add(e.get(i));
if(--n == 1) break;
}
return ans;
} void MAIN() {
int T;
T = cin.nextInt();
while(T --> 0) {
@SuppressWarnings("unchecked")
ArrayList<Integer> subn[] = new ArrayList[8];
for(int i = 0; i < 8; i++) subn[i] = new ArrayList<Integer>();
int n = cin.nextInt();
int q = cin.nextInt();
for(int m, i = 0; i < q; i++) {
m = cin.nextInt();
cost[i] = cin.nextInt();
for(int t = 0; t < m; t++) subn[i].add(cin.nextInt()-1);
}
for(int i = 0; i < n; i++) {
x[i] = cin.nextInt();
y[i] = cin.nextInt();
} ArrayList<Edge> edge = new ArrayList<Edge>();
for(int i = 0; i < n; i++) {
for(int t = i+1; t < n; t++) {
Edge e = new Edge();
e.f = i; e.b = t;
e.v = (x[i]-x[t])*(x[i]-x[t]) + (y[i]-y[t])*(y[i]-y[t]);
edge.add(e);
}
} ArrayList<Edge> used = new ArrayList<Edge>();
for(int i = 0; i < n; i++) pre[i] = i;
Collections.sort(edge);
int ans = MST(n, edge, used);
for(int s = 1; s < (1<<q); s++) {
for(int i = 0; i < n; i++) pre[i] = i; // 初始化并查集
int remain = n, c = 0;
for(int i = 0; i < q; i++) if((s&(1<<i)) > 0) {
c += cost[i];
for(int t = 1; t < subn[i].size(); t++) {
int pf = seek(subn[i].get(0)), pb = seek(subn[i].get(t));
if(pf == pb) continue;
pre[pf] = pb;
remain--;
}
}
ans = min(ans, c + MST(remain, used, null));
}
System.out.println(ans);
if(T > 0) System.out.println();
}
} public static void main(String args[]) { new Main().MAIN(); }
}

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

  1. UVA 1151 Buy or Build MST(最小生成树)

    题意: 在平面上有n个点,要让所有n个点都连通,所以你要构造一些边来连通他们,连通的费用等于两个端点的欧几里得距离的平方.另外还有q个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相 ...

  2. UVA 1151 Buy or Build (最小生成树)

    先求出原图的最小生成树,然后枚举买哪些套餐,把一个套餐内的点相互之间边权为0,直接用并查集缩点.正确性是基于一个贪心, 在做Kruskal算法是,对于没有进入最小生成树的边,排序在它前面的边不会减少. ...

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

    题意: 要使n个点之间能够互通,要使两点直接互通需要耗费它们之间的欧几里得距离的平方大小的花费,这说明每两个点都可以使其互通.接着有q个套餐可以选,一旦选了这些套餐,他们所包含的点自动就连起来了,所需 ...

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

    最小生成树算法简单 只是增加了一些新的东西,对于需要最小生成树算法 和中 并检查使用的一系列 还有一些更深入的了解. 方法的一些复杂问题 #include<cstdio> #include ...

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

    题意:给定n个点,你的任务是让它们都连通.你可以新建一些边,费用等于两点距离的平方(当然越小越好),另外还有几种“套餐”,可以购买,你购买的话,那么有些边就可以连接起来, 每个“套餐”,也是要花费的, ...

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

    题意:给出n个点的坐标,现在需要让这n个点连通,可以直接在点与点之间连边,花费为两点之间欧几里得距离的平方,也可以选购套餐,套餐中所含的点是相互连通的 问最少的花费 首先想kruskal算法中,被加入 ...

  7. UVA - 1151 Buy or Build (买还是建)(并查集+二进制枚举子集)

    题意:平面上有n个点(1<=n<=1000),你的任务是让所有n个点连通.可以新建边,费用等于两端点欧几里德距离的平方.也可以购买套餐(套餐中的点全部连通).问最小费用. 分析: 1.先将 ...

  8. UVA 1151 买还是建(最小生成树)

    买还是建 紫书P358 [题目链接]买还是建 [题目类型]最小生成树 &题解: 这题真的心累,看了3天,最后照着码还是wa,先放lrj代码,以后再看吧 &代码: // UVa1151 ...

  9. UVA 1151二进制枚举子集 + 最小生成树

    题意:平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此, 你可以新建一些边,费用等于两个端点的欧几里得距离的平方.另外还有q(0<=q<=8)个套餐(数 ...

随机推荐

  1. QSS为Qt程序添加不一样的样式

    添加 QSS 样式文件 在 Qt 项目中新建一个或使用已有的 Qt Resource File,在资源文件下面新建一个普通文件,命名为 Light.qss: 为 Light.qss 添加如下内容: 这 ...

  2. 很小的一个函数执行时间调试器Timer

    对于函数的执行性能(这里主要考虑执行时间,所耗内存暂不考虑),这里写了一个简单的类Timer,用于量化函数执行所耗时间. 整体思路很简单,就是new Date()的时间差值.我仅仅了做了一层简单的封装 ...

  3. 三年从前端小工到架构-知乎 Live 学习整理

    最近在知乎上学习了vczero (王利华,簋谣)的知乎Live「三年从前端小工到架构」,感觉受益匪浅,现将本次Live学习笔记记录如下. 本次 Live 主要包括以下内容   • 0-3 年的前端工程 ...

  4. 二、单层感知器和BP神经网络算法

    一.单层感知器 1958年[仅仅60年前]美国心理学家FrankRosenblant剔除一种具有单层计算单元的神经网络,称为Perceptron,即感知器.感知器研究中首次提出了自组织.自学习的思想, ...

  5. vue-cli项目打包多个与static文件同级的静态资源目录(copy-webpack-plugin插件的使用)

    场景 业务要求能够直接通过 "域名+/file"的方式访问静态资源的html,然而产品绝对static暴露在url中不好看又不能直接将html放在static中.所以想到了既然st ...

  6. java 多线程执行过程

    1.分支线程执行 过程: 2.线程运行的状态:五大状态 线程: 从新建状态  就绪状态   运行状态  挂起(阻塞)状态 死亡状态(结束,销毁) 3. 多线程:在同一个时间执行多个任务的操作,现在的软 ...

  7. Django—middleware

    一.Django中间件的请求周期 我们从浏览器发出一个请求 Request,得到一个响应后的内容 HttpResponse ,这个请求传递到 Django的过程如下: 也就是说,每一个请求都是先通过中 ...

  8. JavaScript中实现DI的原理

    什么是依赖注入 按照上面图的流程中我们可以知道我们需要实现这么几件事: 提供一个服务容器 为目标函数注册需要的依赖 获取目标函数注册的依赖项 通过依赖项来查询对应服务 将获取的依赖项传入目标函数 提供 ...

  9. QQ 聊天机器人小薇 2.0.0 发布!

    本次发布主要加入了支持讨论组聊天,并增强了稳定性.另外,官方小薇 QQ 机器人已经下线,大家要体验的话请 自建私服~ 简介 XiaoV(小薇)是一个用 Java 写的 QQ 聊天机器人 Web 服务, ...

  10. P vs NP

    Complexity Class Computational problem Decision Problems Model of computation Time-complexity classe ...