P2872
首先
题目概括:题目让着求使所有牧场都联通.需要修建多长的路.
显然这是一道最小生成树板子题(推荐初学者做).
那我就说一下kruskal吧.
Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表。
用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪心算法的应用。
和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。------- 来自于百度百科
一、基本思路
kruskal利用了一种贪心的思想,先把每一条边按照边权排一下序,利用并查集维护每一个点.
跑kruskal的时候先判断两个点是不是在一个集合里边,如果在那就说明不用再去连边了.
然后合并的时候记录边权,在搞一个记录加的边数的计数器.
大家都知道一张图如果有\(n\)个节点,那么最少\(n-1\)条边就可以吧这张图搞联通了.
那么我们就可以等到计数器的计数记到\(n-1\) 的时候停止执行(已经得到正解).
然后因为这\(n-1\)条边把图连成一起,那么显然\(n - m\)条边就可以把图分成m个部分(很好想鸭).例题:P1195
二、代码
for (int i = 1; i <= cnt; i++) { if (father(edge[i].x) != father(edge[i].y)) {//判断是不是在一个集合中 f++; unionn(edge[i].x, edge[i].y);//合并 ans += edge[i].dis;//记录总权值 } if (f == m) break;//如果做完了,那就停下啊. }
此题代码及思路:
因为有一些边是一开始就有的,那么我们可以吧一开始就有的那些边都赋值成0,然后继续跑kruskal就好了.
因为给出的是坐标,那就先把坐标都存起来,然后把这些坐标依照欧几里得距离两两建边.
欧几里得距离公式:\(\sqrt{((x_{1}-x_{2})*(x_{1}-x_{2}) + (y_{1}-y_{2}) * (y_{1}-y_{2}))}\)
#include <bits/stdc++.h> #define N 1000010 #define M 2010 using namespace std; int fath[M], n, m; bool b[M]; double px[M], py[M]; struct node {//结构体存边. int x, y; double dis; }edge[N << 2]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int father(int x) { if (x != fath[x]) fath[x] = father(fath[x]);//求是不是在一个集合里 return fath[x]; } void unionn(int x, int y) { int fx = father(x), fy = father(y);//合并两个集合 fath[x] = fath[y]; } bool cmp(node p, node q) { return p.dis < q.dis;//sort用品 } int main() { n = read(), m = read(); int z = n + m;//原本就有的 for (int i = 1; i <= z; i++) fath[i] = i; int cnt = 0; for (int i = 1, x, y; i <= n; i++) { x = read(), y = read(); px[i] = x, px[i] = y;//因为给出的是坐标,先把坐标存起来. } for (int i = 1; i <= n; i++) fath[i] = 1; for (int i = n + 1, x, y; i <= n + m; i++) { x = read(), y = read(); px[i] = x, py[i] = y; } for (int i = 1; i <= n + m; i++) { for (int j = i + 1; j <= n + m; j++) {//开始存边 cnt++; edge[cnt].x = i; edge[cnt].y = j; edge[cnt].dis = sqrt((px[i] - px[j]) * (px[i] - px[j]) + (py[i] - py[j]) * (py[i] - py[j])); } } sort(edge + 1, edge + cnt + 1, cmp);//给边排一下序 int f = 0; double ans = 0; for (int i = 1; i <= cnt; i++) {//kruskal if (father(edge[i].x) != father(edge[i].y)) { f++; unionn(edge[i].x, edge[i].y); ans += edge[i].dis; } if (f == m) break; } printf("%.2lf", ans); }