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

分析:

1、先将不购买任何套餐的最小生成树的所有边(边数为cnt)存起来,目的是枚举套餐时不必再耗Kruskal算法的O(n2)复杂度,而是降低为O(cnt)。

2、二进制枚举套餐。

3、枚举套餐时,先将套餐中的边按最小生成树建边,在将不购买任何套餐的最小生成树的cnt条边建上,因为套餐中的边权值为0,所以这样处理不会影响结果。

#pragma comment(linker, "/STACK:102400000, 102400000")
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
#define Min(a, b) ((a < b) ? a : b)
#define Max(a, b) ((a < b) ? b : a)
const double eps = 1e-8;
inline int dcmp(double a, double b){
if(fabs(a - b) < eps) return 0;
return a > b ? 1 : -1;
}
typedef long long LL;
typedef unsigned long long ULL;
const int INT_INF = 0x3f3f3f3f;
const int INT_M_INF = 0x7f7f7f7f;
const LL LL_INF = 0x3f3f3f3f3f3f3f3f;
const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;
const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};
const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};
const int MOD = 1e9 + 7;
const double pi = acos(-1.0);
const int MAXN = 1000 + 10;
const int MAXT = 10000 + 10;
using namespace std;
vector<int> v;//不选择套餐时最小生成树的边编号
int ans, n, m;
int fa[MAXN];
struct City{//城市
int x, y, id;
void read(){
scanf("%d%d", &x, &y);
}
}num[MAXN];
struct Package{//套餐
int n, cost;
int city[MAXN];
void read(){
scanf("%d%d", &n, &cost);
for(int i = 0; i < n; ++i){
scanf("%d", &city[i]);
}
}
}q[10];
struct Edge{
int from, to, dist;
void set(int f, int t, int d){
from = f;
to = t;
dist = d;
}
bool operator < (const Edge& rhs)const{
return dist < rhs.dist;
}
}e[MAXN * MAXN];
int getD(City& A, City& B){
return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y);
}
int Find(int v){
return fa[v] = (fa[v] == v) ? v : Find(fa[v]);
}
void solve(){//二进制枚举子集
for(int i = 1; i < (1 << m); ++i){
int tmp = 0;
for(int j = 1; j <= n; ++j) fa[j] = j;//初始化并查集
for(int j = 0; j < m; ++j){
if(i & (1 << j)){
tmp += q[j].cost;
for(int a = 0; a < q[j].n; ++a){
for(int b = a + 1; b < q[j].n; ++b){
int x = Find(q[j].city[a]);
int y = Find(q[j].city[b]);
if(x == y) continue;
if(x < y) fa[y] = x;
else fa[x] = y;
}
}
}
}
int len = v.size();
for(int j = 0; j < len; ++j){
int id = v[j];
int x = Find(e[id].from);
int y = Find(e[id].to);
if(x == y) continue;
tmp += e[id].dist;
if(x < y) fa[y] = x;
else fa[x] = y;
}
ans = Min(ans, tmp);
}
}
int main(){
int T;
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
for(int i = 0; i < m; ++i){
q[i].read();
}
for(int i = 1; i <= n; ++i){
num[i].read();
fa[i] = i;
}
int cnt = 0;
for(int i = 1; i <= n; ++i){
for(int j = i + 1; j <= n; ++j){
e[cnt++].set(i, j, getD(num[i], num[j]));
}
}
sort(e, e + cnt);
v.clear();
ans = 0;
for(int i = 0; i < cnt; ++i){
int x = Find(e[i].from);
int y = Find(e[i].to);
if(x == y) continue;
ans += e[i].dist;
v.push_back(i);
if(x < y) fa[y] = x;
else fa[x] = y;
}
solve();
printf("%d\n", ans);
if(T) printf("\n");
}
return 0;
}

  

UVA - 1151 Buy or Build (买还是建)(并查集+二进制枚举子集)的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 紫书 例题 11-3 UVa 1151 (有边集的最小生成树+二进制枚举子集)

    标题指的边集是说这道题的套餐, 是由几条边构成的. 思路是先做一遍最小生成树排除边, 因为如果第一次做没有加入的边, 到后来新加入了很多权值为0的边,这些边肯定排在最前面,然后这条边的前面的那些边肯定 ...

随机推荐

  1. 二、点击导出按钮创建excle写入内容后下载功能实现

    /*涉及的jar包1)biframework.jar用于实现分页功能2)poi-3.7-20101029.jar:读取.创建.修改excle.word.ppt的Java APIApache POI是创 ...

  2. 解决maven项目创建过慢的问题,一直resolving dependencies...

    ##方法一: 1.在创建项目时设置其Properties (我大多在创建项目时 就解决这个问题) 创建项目的时候,在Properties属性面板中添加一项:archetypeCatalog = int ...

  3. Unity Scene视图下 输出物体坐标等信息

    using UnityEditor; using UnityEngine; [CustomEditor(typeof(GameObject))] public class MyEditor : Edi ...

  4. 第3节 sqoop:3、sqoop的入门测试使用

    3.5. Sqoop的数据导入 “导入工具”导入单个表从RDBMS到HDFS.表中的每一行被视为HDFS的记录.所有记录都存储为文本文件的文本数据(或者Avro.sequence文件等二进制数据) 列 ...

  5. day08-Python运维开发基础(文件操作与相关函数、函数基础)

    1. 文件操作及相关函数 # ### 文件操作 """ fp = open("文件名称",mode=模式,encoding=编码集) fp 文件io对 ...

  6. [Codeforces #608 div2]1271A Suits

    Description A new delivery of clothing has arrived today to the clothing store. This delivery consis ...

  7. 从零开始学C++(1 变量和基本类型)

    接下来的几篇文章介绍C++的基础知识点. C++是一种静态数据类型语言,它的类型检查发生在编译时.因此,编译器必须知道程序中每一个变量对应的数据类型. 数据类型是程序的基础:它告诉我们数据的意义以及我 ...

  8. 12.redis的AOF持久化深入讲解各种操作和相关实验

    1.AOF持久化的配置 2.AOF持久化的数据恢复实验3.AOF rewrite4.AOF破损文件的修复5.AOF和RDB同时工作 ---------------------------------- ...

  9. 解决fedora28桌面图标问题

    正文 在fedora28中默认是没有桌面图标的,对于那些习惯使用桌面的图标的人来说使用有点不适应. 替代方法是: 下载nemo,在终端内输入sudo dnf install nemo 创建~/.con ...

  10. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 字体图标(Glyphicons):glyphicon glyphicon-inbox

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...