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

题意:要使n个点之间能够互通,要使两点直接互通需要耗费它们之间的欧几里得距离的平方的权(注意不需要开方的),这说明每两个点都可以使其互通。接着又q个套餐可以选,一旦选了这些套餐,他们所包含的点自动就连起来了,所需要做的就是连上还未通的即可,q<=8。可以多买。求最小生成树所需的代价。

思路:与普通求MST不同的就是多了套餐,而且还可以多买。每个套餐有买或不买两种可能,那么有28种可能,即256种。

  如果不买套餐,至少需要求1次MST是确定的,这个复杂度已经是O(n*n)了。还得考虑哪些餐套可以搭配来买更便宜,那么就穷举这256种组合,每种组合来一次MST,但是不再需要O(n*n)了,只需要用第一次生成树时所挑出来的边即可,那些就是MST了,不挑他们挑谁。

  具体做法是,将套餐内的所有点先连接(并查集),再用MST的边来一次kruscal(记得加上套餐费)。对于每个组合都这样做,就能求出结果了。

  特别要注意:每两个输出结果之间要1个空行,末尾不需要再空行,否则出错。

  1 #include <bits/stdc++.h>
  2 #define   LL long long
  3 using namespace std;
  4 const int N=1000+5;
  5 const int INF=0x7f7f7f7f;
  6 vector<int> vect[10];
  7 vector< pair<int,int> > cor, e, tree;
  8 int t, r, n, q, a, b;
  9 int cost[10],  pre[N],  g[N][N];;
 10
 11 int cmp(pair<int,int> a,pair<int,int> b){return g[a.first][a.second]<g[b.first][b.second]? true: false;}//按照距离来排序
 12 int dis( pair<int,int> a,pair<int,int> b ){return  (a.first-b.first)*(a.first-b.first) +(a.second-b.second)*(a.second-b.second) ;}//不需要开方
 13
 14 int find(int x){return pre[x]==x? x: pre[x]=find(pre[x]);}         //查
 15 void joint(int a,int b){a=find(a),b=find(b);if(a!=b)    pre[a]=b;} //并
 16
 17
 18
 19 LL kruscal()  //将生成树的树边取出
 20 {
 21     for(int i=1; i<=n; i++)     pre[i]=i;
 22     int cnt=0;
 23     LL sum=0;
 24     for(int i=0; i<e.size(); i++)
 25     {
 26         int a=e[i].first;
 27         int b=e[i].second;
 28         if(find(a)!=find(b))
 29         {
 30             cnt++;
 31             tree.push_back(e[i]);   //收藏边
 32             sum+=g[a][b];           //统计权值
 33             joint(a,b); //a和b是点
 34             if(cnt>=n-1)    return sum;
 35         }
 36     }
 37     return sum;
 38 }
 39
 40
 41 LL kruscal_2()  //带套餐的
 42 {
 43     LL sum=0;
 44     for(int i=0; i<tree.size(); i++)
 45     {
 46         int a=tree[i].first;
 47         int b=tree[i].second;
 48         if(find(a)!=find(b))
 49         {
 50             sum+=g[a][b];
 51             joint(a,b);
 52         }
 53     }
 54     return sum;
 55 }
 56
 57 LL cal()
 58 {
 59     sort(e.begin(), e.end(), cmp);
 60     tree.clear();
 61     LL ans=kruscal();      //第一次生成树,挑出有用边
 62     int choice=1;
 63     while(q--)  choice+=choice;
 64     for(int i=1; i<choice; i++)
 65     {
 66         for(int j=1; j<=n; j++)     pre[j]=j;
 67         int tmp=i, cnt=1;
 68         LL sum=0;
 69         while(tmp)      //先将欲买套餐的pre归类
 70         {
 71             if((tmp&1)==1)  //第cnt个套餐要了
 72             {
 73                 sum+=cost[cnt];
 74                 for(int j=1; j<vect[cnt].size(); j++)    joint(vect[cnt][j-1],vect[cnt][j]);
 75             }
 76             tmp>>=1;
 77             cnt++;
 78         }
 79         ans=min(ans, sum+kruscal_2());  //再生成树
 80     }
 81     return ans;
 82 }
 83
 84 int main()
 85 {
 86     freopen("input.txt", "r", stdin);
 87     cin>>t;
 88     while(t--)
 89     {
 90         cin>>n>>q;
 91         for(int i=1; i<=q; i++)         //每个套餐
 92         {
 93             scanf("%d%d",&a,&cost[i]);
 94             vect[i].clear();
 95             while(a--)
 96             {
 97                 scanf("%d",&r);
 98                 vect[i].push_back(r);
 99             }
100         }
101         cor.clear();
102         for(int i=0; i<n; i++)
103         {
104             scanf("%d%d",&a,&b);
105             cor.push_back(make_pair(a,b));  //每个点的坐标
106         }
107
108         memset(g, 0, sizeof(g));
109         e.clear();
110         for(int i=1; i<=n; i++)              //计算出距离
111         {
112             for(int j=i+1; j<=n; j++)
113             {
114                 g[i][j]=g[j][i]= dis(cor[i-1],cor[j-1]);
115                 e.push_back(make_pair(i,j));
116             }
117         }
118         cout<<cal()<<endl;
119         if(t) printf("\n");
120     }
121     return 0;
122 }

AC代码

时间: 2024-10-20 00:55:22

UVA 1151 Buy or Build (MST最小生成树,kruscal,变形)的相关文章

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

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

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

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

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

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