标签: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