最小生成树(基础)

技术文章 11个月前 完美者
1,116 0

标签:for   span   tar   main   lan   水水的   cout   算法思想   target   

最小生成树(基础知识

由于不知道今年考不考最小生成树,于是我们教练让学,让学的话那我就学吧,这就是我与最小生成树的邂逅bushi

Prim算法

同班的几位巨巨说,由于Prim朴素算法时间复杂度太高(O(n2)),优化过后与Kruskal是竞争关系,所以不是经常使用,但是算法思想我还是了解了一下,大概是这样的,先确定一个点,再去找以这个点为起点的,以没有被选过的点为终点的边权最小的边,然后再以那个终点为起点,来更新整个数组(即是连通块到点的最短距离),直到所有的点都被标记了。这种算法与Dijkstra有点相似,只不过更新的东西不一样。

还是贴个水水的代码吧

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 510, INF = 0x3f3f3f3f;
 4 int n,m;
 5 bool st[N]; //表示点i是否在当前生成树集合中
 6 int dist[N]; //表示点i到当前集合的最短边的长度
 7 int g[N][N]; //表示点i和点j之间边的长度
 8 // 返回值:最小生成树中所有边的总长度
 9 int Prim(){
10     memset(dist, 0x3f, sizeof dist);
11     int res = 0;
12     for (int i = 0; i < n; i ++ ){
13         int id = -1;// 寻找最短边
14         for (int j = 1; j <= n; j ++ )
15             if (!st[j] && (id==-1 || dist[j] < dist[id]))
16                 id = j;
17         if(i&&dist[id]==INF) return INF;
18         if(i) res += dist[id];// 用新加入的点更新其余点到生成树的最短边
19         for (int j = 1; j <= n; j ++ ){
20             dist[j] = min(dist[j], g[id][j]);
21         }
22         st[id] = true;
23     }
24     return res;
25 }
26 int main(){
27     scanf("%d%d", &n, &m);
28     memset(g, 0x3f, sizeof g);
29     while(m--){
30         int a, b, c;
31         scanf("%d%d%d", &a, &b, &c);
32         g[a][b] = g[b][a] = min(g[a][b], c);
33     }
34     int t = Prim();
35     if(t==INF) puts("impossible");//没有最小生成树 
36     else printf("%d\n",t);
37     return 0;
38 }

 

由于太水,就没有贴优化过的代码了。话说这算法在稠密图时效率还蛮高的 它大概也只有这一个好处了吧

Kruskal算法

这个算法时间复杂度就低一些了,前提是这个图不是很稠密,m没有大到离谱。思路较为清晰,因为每次插入的树的边都是当前边权最小的一条边,所以用一个sort就一劳永逸了,每次看一下插入的边的起点和终点在不在树上(具体用并查集的get操作<就是询问当前节点的根>实现),如果在树上的话,那么两者的根应该是同一个,如果不是这样的话,那么就有一个不在树上,于是我们就将此节点放到树上(具体用并查集的merge操作实现),树的边数就要更新。将所有的边遍历完成之后,如果树的边数小于点数减一,那么这个图就没有最小生成树。      代码实现还是比较容易的,总体来说就是并查集+sort排序。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int M=200009,INF=0x3f3f3f3f;
 4 int n,m,fa[5009];
 5 struct node{//结构体储存边,a起点,b终点,z边权 
 6     int a,b,z;
 7 }edge[M];
 8 bool cmp(node x,node y){//cmp 
 9     return x.z<y.z;
10 }
11 int get(int x){//并查集找其根节点 
12     if(x==fa[x]) return x;
13     return fa[x]=get(fa[x]);
14 }
15 void mer(int x,int y){//并查集合并 
16     fa[get(x)]=get(y);
17 }
18 int KRU(){//kruskal算法,最小生成树 
19     sort(edge+1,edge+1+m,cmp);
20     for(int i=1;i<=n;i++) fa[i]=i;
21     int cnt=0,sum=0;
22     for(int i=1;i<=m;i++){
23         int x=edge[i].a,y=edge[i].b,w=edge[i].z;
24         x=get(x),y=get(y);
25         if(x!=y){
26             mer(x,y);
27             sum+=w; 
28             cnt++;
29         } 
30     }
31     if(cnt<n-1) return -1;
32     return sum;
33 }
34 int main(){
35     cin>>n>>m;
36     for(int i=1;i<=m;i++){
37         cin>>edge[i].a>>edge[i].b>>edge[i].z;
38     }
39     int kk=KRU();
40     if(kk==-1) cout<<"orz";
41     else cout<<kk;
42     return 0;
43 }

 

这个代码就是luogu上的版题 

今天A的题:luoguP3366,P2330,P1546,P2872,P1547,P2820,P1991


 

今天就学了最小生成树的基础,总体来说掌握的海星,时间也不是很紧吧,可

最小生成树(基础)

标签:for   span   tar   main   lan   水水的   cout   算法思想   target   

原文地址:https://www.cnblogs.com/001A/p/13898995.html

版权声明:完美者 发表于 2020-10-30 12:43:08。
转载请注明:最小生成树(基础) | 完美导航

暂无评论

暂无评论...