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

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

首先想kruskal算法中,被加入的边已经是最优的了,所以当选择完套餐后,之前被丢弃的边也不会再进入最小生成树

然后就可以先求一次原图的最小生成树,保存下进入最小生成树的n-1条边

再枚举选择的套餐的情况,再求最小生成树,这里用的二进制法枚举 最后维护一个最小值就可以了

思路虽然看懂了,可是代码根本就写不出来,看着标程写的,最后还是改了那么久-- sad----------

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include <cmath>
  5 #include<stack>
  6 #include<vector>
  7 #include<map>
  8 #include<set>
  9 #include<queue>
 10 #include<algorithm>
 11 using namespace std;
 12
 13 #define foreach(i,c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i)
 14
 15 typedef long long LL;
 16 const int INF = (1<<30)-1;
 17 const int mod=1000000007;
 18 const int maxn=1005;
 19 const int maxq=8;
 20
 21 int n;
 22 int x[maxn],y[maxn],cost[maxn];
 23 vector<int> subn[maxn];
 24
 25 int p[maxn];
 26 int find(int x) {return p[x]!=x? p[x]=find(p[x]):x;}
 27
 28 struct edge{
 29     int u,v,d;
 30     edge(int u,int v,int d):u(u),v(v),d(d) {}
 31     bool operator <(const edge& rhs) const{
 32     return d<rhs.d;}
 33 };
 34
 35 int mst(int cnt,const vector<edge>& e,vector<edge>& used){
 36     if(cnt==1) return 0;
 37     int m=e.size();
 38     int ans=0;
 39     used.clear();
 40     for(int i=0;i<m;i++){
 41         int u=find(e[i].u),v=find(e[i].v);
 42         int d=e[i].d;
 43         if(u!=v){
 44             p[u]=v;
 45             ans+=d;
 46             used.push_back(e[i]);
 47             if(--cnt==1) break;
 48         }
 49     }
 50     return ans;
 51 }
 52
 53 int main(){
 54 //    freopen("in.txt","r",stdin);
 55 //    freopen("outttttttt.txt","w",stdout);
 56     int T,q;
 57     scanf("%d",&T);
 58     while(T--){
 59         scanf("%d%d",&n,&q);
 60         for(int i=0;i<q;i++){
 61             int cnt;
 62             scanf("%d %d",&cnt,&cost[i]);
 63             subn[i].clear();
 64             while(cnt--){
 65                 int u;
 66                 scanf("%d",&u);
 67                 subn[i].push_back(u-1);
 68             }
 69         }
 70
 71         for(int i=0;i<n;i++) scanf("%d %d",&x[i],&y[i]);
 72
 73         vector<edge> e,need;
 74
 75         for(int i=0;i<n;i++)
 76          for(int j=i+1;j<n;j++){
 77              int c=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
 78              e.push_back(edge(i,j,c));
 79          }
 80
 81         for(int i=0;i<n;i++) p[i]=i;
 82         sort(e.begin(),e.end());
 83
 84         int ans=mst(n,e,need);
 85
 86         for(int mask=0;mask<(1<<q);mask++){
 87
 88             for(int i=0;i<n;i++) p[i]=i;
 89             int cnt=n,c=0;
 90
 91             for(int i=0;i<q;i++) if(mask & (1<<i)){
 92                 c+=cost[i];
 93                 for(int j=1;j<subn[i].size();j++){
 94                     int u=find(subn[i][j]),v=find(subn[i][0]);
 95                     if(u!=v){p[u]=v;cnt--;}
 96                 }
 97             }
 98             vector<edge> dummy;
 99             ans=min(ans,c+mst(cnt,need,dummy));
100         }
101         printf("%d\n",ans);
102         if(T) printf("\n");
103     }
104     return 0;
105 }

时间: 2024-10-14 07:10:23

UVa 1151 Buy or Build【最小生成树】的相关文章

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个套餐的花费为C

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

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

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 ,可以接受. 有一个地方我不知道是不