UVa 1151 买还是建

https://vjudge.net/problem/UVA-1151

题意:

平面上有n个点,你的任务是让所有n个点连通。为此,你可以新建一些边,费用等于两个端点的距离平方和。另外还有q个套餐可以购买,如果你购买了第i个套餐,该套餐中的所有结点都变得相互连通,第i个套餐的花费为Ci。

思路:

这道题比较容易超时。可能需要用到并查集的路径压缩,我下面的代码就是用了路径压缩,不然要超时。也是看了别人的代码才知道还有这种省时间的做法。

先介绍一下路径压缩吧:

如果并查集像一字长蛇这样排列的话,寻找起来就比较费时间,但如果像图2一样的话,一下子就可以找到根了。压缩的方法也是挺简单的。

1     int r = x;
2     while (r != p[r])  r = p[r];
3     int i = x, j;
4     while (p[i] != r)
5     {
6         j = p[i];
7         p[i] = r;
8         i = j;
9     }

题目的做法就像紫书上说的那样,先不考虑套餐算一遍,然后枚举套餐的方法,这里的话二进制枚举法非常方便。

  1 #include<iostream>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 using namespace std;
  6
  7 const int maxn = 1000 + 5;
  8
  9 int n, m, q, cnt;
 10 int p[maxn];
 11 vector<int> g[10];  //方案集合
 12 int c[10];          //方案价格
 13
 14 //边
 15 struct node
 16 {
 17     int u;
 18     int v;
 19     int dist;
 20 }edge[maxn*maxn];
 21
 22 //点
 23 struct node2
 24 {
 25     int x, y;
 26 }a[maxn];
 27
 28 int find(int x)
 29 {
 30     //return p[x] == x ? x : find(p[x]);   用这个会超时
 31     //路径压缩
 32     int r = x;
 33     while (r != p[r])  r = p[r];
 34     int i = x, j;
 35     while (p[i] != r)
 36     {
 37         j = p[i];
 38         p[i] = r;
 39         i = j;
 40     }
 41     return r;
 42 }
 43
 44 bool cmp(node a, node b)
 45 {
 46     return a.dist < b.dist;
 47 }
 48
 49 //计算距离平方和
 50 int cacl_dist(node2 a, node2 b)
 51 {
 52     return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
 53 }
 54
 55 void init()
 56 {
 57     for (int k = 1; k <= n; k++)   p[k] = k;
 58 }
 59
 60 int Kruskal()
 61 {
 62     int num = 0;
 63     int ans = 0;
 64     for (int i = 0; i < cnt ; i++)
 65     {
 66         int x = find(edge[i].u);
 67         int y = find(edge[i].v);
 68         if (x != y)
 69         {
 70             p[x] = y;
 71             ans += edge[i].dist;
 72             num++;
 73         }
 74         if (num == n - 1)  return ans;
 75     }
 76     return ans;
 77 }
 78
 79
 80 void solve()
 81 {
 82     init();
 83     int ans = Kruskal();
 84     //二进制枚举方案
 85     for (int i = 0; i < (1 << q); i++)
 86     {
 87         init();
 88         int cost = 0;
 89         for (int j = 0; j < q; j++)
 90         {
 91             if (i & (1 << j))
 92             {
 93                 cost += c[j];
 94                 int x = find(g[j][0]);
 95                 for (int k = 1; k < g[j].size(); k++)
 96                 {
 97                     int y = find(g[j][k]);
 98                     if (x != y)
 99                         p[y] = x;
100                 }
101             }
102         }
103         ans = min(cost + Kruskal(), ans);
104     }
105     printf("%d\n", ans);
106 }
107
108 int main()
109 {
110     //freopen("D:\\txt.txt", "r", stdin);
111     int T, t, s, kase=0;
112     scanf("%d", &T);
113     while (T--)
114     {
115         if (++kase > 1)    printf("\n");
116         scanf("%d%d", &n, &q);
117
118         //存储方案
119         for (int i = 0; i < q; i++)
120         {
121             g[i].clear();
122             scanf("%d", &t);
123             scanf("%d", &c[i]);
124             for (int j = 0; j < t; j++)
125             {
126                 scanf("%d", &s);
127                 g[i].push_back(s);
128             }
129         }
130
131         for (int i = 1; i <= n; i++)
132             scanf("%d%d", &a[i].x, &a[i].y);
133
134         //存储边
135         cnt = 0;
136         for (int i = 1; i <= n;i++)
137         for (int j = i + 1; j <= n; j++)
138         {
139             edge[cnt].u = i;
140             edge[cnt].v = j;
141             edge[cnt].dist = cacl_dist(a[i], a[j]);
142             cnt++;
143         }
144         sort(edge, edge + cnt, cmp);
145         solve();
146     }
147     return 0;
148 }
时间: 2024-10-10 16:07:34

UVa 1151 买还是建的相关文章

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),这个时间复杂度太大了吧,肯定会超时, 那么我们就可以优化一下,首先先算出来最小生成树,并且把每条边都保存下来,那么加了“套餐”之后

POJ 1149 猪圈买猪 建图太强大!! 没有透彻领悟 慢慢消化

PIGS Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 19575   Accepted: 8948 Description Mirko works on a pig farm that consists of M locked pig-houses and Mirko can't unlock any pighouse because he doesn't have the keys. Customers come t

uva 1151(最小生成树,枚举子集)

题意:平面上有n个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方.另外还有q(0<=q<=8)个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci. kruskal: 先求一次原图的最小生成树,得到n-1条边,然后每次枚举完套餐后只考虑套餐中的边和这n-1条边,则枚举套餐之后再求最小生成树. key: kruskal算法中,那些两端已经属于同一个连通分量的边不会再加到

UVA 1151

/* 题意:有n个点,现在需要联通所有,有q种套餐可以选择, 当然套餐之外也可以自己添加边,意为达到最短距离. 题意很明显,不知道需要使用哪一种套餐, 那么需要枚举每一种套餐的情况. 然后再进行对比. 注意最开始没有套餐的情况. */ #include<cstdio> #include<iostream> #include<algorithm> #include<vector> using namespace std; const int maxn = 11

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个点(1<=N<=1000),你的任务是让所有n个点连通,为此,你可以新建一些边,费用等于两个端点的欧几里得距离的平方. 另外还有q(0<=q<=8)个套餐,可以购买,如果你购买了第i个套餐,该套餐中的所有结点将变得相互连通,第i个套餐的花费为ci. 求最小花费. Input (1 ≤ n ≤ 1000)  (0 ≤ q ≤ 8). The second integer is the the cost of the subnetwork(not greater

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

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