E.pXIphp

【算法随笔】最小生成树

最小生成树(MST):[洛谷模版传送门]

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。

——度娘百度百科

说白了就是给你一个图,把边权等都给你(特殊情况是相等的)。然后让这些节点全部都连成一颗树,问最小的代价是多少。

本篇blog会对kruskal算法进行详细讲解。其实是Prim掌握还不熟

kruskal算法是用的比较多的一类求最小生成树的算法。核心思想是贪心。程序要做的重要两步:

1.给边排序,比较的标准是边的权值,小的在前面。

2.开始从排序后开始枚举边,如果这条边的起点和终点已经联通,则不管,反之则加入。

为什么这个方法是对的呢?

请你思考一下:每个可以联通的点,因为已经排好了序,如果节点v还没有加入,现有一条(u,v)的边,肯定比其他到v的权值更小。

 

我们采取“逐个击破”的原则去做出这两步:

一.给边排序

 这里有两种方案可以让边进行快速排序(当然你要手写快排我也不拦你)

1.为sort函数传入第三个参数

有兴趣的同学可以看一下sort

如果有同学问:为什么有两个sort函数?

C++是支持函数重载的,只要参数个数和类型不同,都可以同时存在

然后我们可以定义一个compare函数(其实很复杂的,目前可以先这么理解)

然后我们传参进去,穿的是compare函数的地址。

1 bool cmp(Edge x,Edge y){return x.dis<y.dis;}
2 sort(edge,edge+m,cmp);//注意这个地方cmp后面没有括号和参数!!!

这是第一种定义大小比较。

2.重载运算符'<'

在Edge的结构体内放上这样一行:

bool operator <(const Edge& A)const{
    return dis<A.dis;
}//const修饰符不要忘了!!!

这个时候可以直接sort。

二.枚举边,看终点是否已经连接

 这个看上去很麻烦。。。判断联通是比较困难的,BFS和DFS都可以,但是——TLE会向你招手

那有没有一个比较简单易学的算法来实现呢?

我们发现:如果一个点和另一个点联通,他们之间是可以不管父亲儿子节点。

于是有同学想到了STL的容器set没错那个同学就是我

但是set太low了是错的。因为边排序后,生成(联通)每一条边并不一定相等。

所以我们需要一个高效简介的算法:并查集

我们只需要维护一个f数组,f[u]表示节点u的父亲。

再套一个函数find,用于找到u的最终祖先。

1 int find(int x){return f[x]==x?x:f[x]=find(f[x]);}//简洁
3 f[v]=u;//让v的的父亲变成u

所以,我们以洛谷测试为例:

krusal的过程是这样的:

 

(1)ans+=2;

 

 (2)ans+=2;

 

 

 (3)ans+=3;

下面仍给出代码:

 1 #include<bits/stdc++.h>
 2 namespace Jason{
 3     inline int scan(int &x){
 4     int f=1;x=0;char s=getchar();
 5     while(s<'0' || s>'9'){if(s=='-') f=-1;s=getchar();}
 6     while(s>='0' && s<='9'){x=x*10+s-'0';s=getchar();}
 7     x*=f;
 8     return 1;//返回1说明读入正常 
 9 }
10     inline int print(int x){
11         if(x<0){putchar('-');x=-x;}
12         if(x>9)print(x/10);char s=x%10+'0';
13         putchar(s);
14         return 1;//返回1说明输出正确 
15     }
16 }
17 using namespace std;
18 using namespace Jason;
19 const int maxn=5005;
20 const int maxm=200005;
21 struct Edge{
22     int s,t,dis;
23 bool operator <(const Edge A)const{
24     return dis<A.dis;
25 } 
26 }edge[maxm<<1];
27 int n,m,tot=-1;
28 int f[maxn];
29 //-----------------------------------------------数据结构和读入输出优化 
30 void init(){for(int i=1;i<=n;++i)f[i]=i;}
31 inline int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
32 //-----------------------------------------------并查集相关函数 
33 bool cmp(Edge x,Edge y){return x.dis<y.dis;}
34 void add(int u,int v,int w)
35 {
36     edge[++tot].s=u;
37     edge[tot].t=v;
38     edge[tot].dis=w;
39 }
40 int kruskal(){
41     int ans=0;
42     sort(edge,edge+m);
43     for(int i=0;i<m;++i)
44     {
45         int u=find(edge[i].s),v=find(edge[i].t);
46         if(u!=v)
47         ans+=edge[i].dis;
48         f[v]=u;
49     }
50     return ans;
51 }
52 int main()
53 {
54     scan(n),scan(m);
55     init();
56     for(int i=0,x,y,z;i<m;++i)scan(x),scan(y),scan(z),add(x,y,z);
57     int ans=kruskal();
58     print(ans);
59     putchar('\n');
60     return 0;
61 }

有一个动态算法可视网站[点我进入],里面有MST,Dijkstra,Bellman-Ford等算法,疯狂安利。

 

 

posted @ 2019-01-25 09:36  Ekretn  阅读(208)  评论(0编辑  收藏  举报