最小生成树(模板)
文章目錄
- 關于時間復雜度:
- Kruskal講解
- Kruskal模板
- Prim講解
- Prim模板
昨天做藍橋杯的題,最后一題最小生成樹,但好久沒用一下子生疏了。。。
又過了幾天離散學到了最小生成樹。。。趁此復習一波
最小生成樹兩種方法:
Prim和Kruskal
關于時間復雜度:
prim:該算法的時間復雜度為O(n2)。與圖中邊數無關,該算法適合于稠密圖。
kruskal:需要對圖的邊進行訪問,所以克魯斯卡爾算法的時間復雜度只和邊又關系,可以證明其時間復雜度為O(eloge)。適合稀疏圖。
Kruskal講解
利用并查集來求
連通塊:無向圖中相互連通 的一些點
具體步驟:將一個連通塊當做一個集合,然后按照邊的大小(從小到大)進行排序,一開始認為每個點都是孤立的,自身是一個集合。
然后按照順序枚舉每一個邊,如果這個邊連接了不同的集合,就用并查集將兩個集合合并在一起,就成一個集合了。并將這條邊加入最小生成樹。如果這條邊連接的是一個集合,就直接跳過,直到n-1條邊為止
Kruskal模板
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> using namespace std; int n,m,i,j,u,v,total; struct edge{int start,to;long long val; }bian[2000005]; int f[100000]; long long ans;int find(int x)//并查集部分 {if (f[x]==x) return x; else return f[x]=find(f[x]);}bool cmp(edge a,edge b)//結構體快排時用到的 {return a.val<b.val; }inline void kruskal()//最小生成樹 {for(int i=1;i<=m;i++){u=find(bian[i].start);v=find(bian[i].to);if(u==v) continue;//判斷在不在同一個并查集里面,在就下一個循環ans+=bian[i].val;//不在,就加上f[u]=v;//連接兩個并查集total++;if(total==n-1) break;//當形成了最小生成樹后,退出(之后做的也沒用了)} } int main() {scanf("%d%d",&n,&m);for(i=1;i<=n;i++) f[i]=i;for(i=1;i<=m;i++){scanf("%d%d%d",&bian[i].start,&bian[i].to,&bian[i].val);}sort(bian+1,bian+m+1,cmp);//快排邊長kruskal();printf("%d",ans);return 0; }Prim講解
Prim中白點代表已經進入最小生成樹的點,藍點代表未進入最小生成樹的二點
MST表示最小生成樹權值
min[v]表示藍點v與白點相連的最小邊權值
Prim算法每次回循環將一個藍點u變成白點,并且此藍點與白點相連的最小邊權值min[u]還是當前所有藍點中最小的,這樣相當于向生成樹中添加了n-1次最小的邊,最后的到的肯定是最小生成樹
步驟:
(a)初始化:min=inf min[1]=0 MST=0
(b)for枚舉點
1.尋找min[u]最小的藍點u
2.將u標記為白點,說明已經進入最小生成樹
3.MST+這條邊min[u]
4.for枚舉與u相連的藍點v
if(w[u][v]<min[v])min[v]=w[u][v]
不斷更新邊權
5.輸出答案
Prim模板
#include<bits/stdc++.h> using namespace std; #define re register #define il inline il int read() {re int x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();return x*f; }//快讀,不理解的同學用cin代替即可 #define inf 123456789 #define maxn 5005 #define maxm 200005 struct edge {int v,w,next; }e[maxm<<1]; //注意是無向圖,開兩倍數組 int head[maxn],dis[maxn],cnt,n,m,tot,now=1,ans; //已經加入最小生成樹的的點到沒有加入的點的最短距離,比如說1和2號節點已經加入了最小生成樹,那么dis[3]就等于min(1->3,2->3) bool vis[maxn]; //鏈式前向星加邊 il void add(int u,int v,int w) {e[++cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt; } //讀入數據 il void init() {n=read(),m=read();for(re int i=1,u,v,w;i<=m;++i){u=read(),v=read(),w=read();add(u,v,w),add(v,u,w);} } il int prim() {//先把dis數組附為極大值for(re int i=2;i<=n;++i){dis[i]=inf;}//這里要注意重邊,所以要用到minfor(re int i=head[1];i;i=e[i].next){dis[e[i].v]=min(dis[e[i].v],e[i].w);}while(++tot<n)//最小生成樹邊數等于點數-1{re int minn=inf;//把minn置為極大值vis[now]=1;//標記點已經走過//枚舉每一個沒有使用的點//找出最小值作為新邊//注意這里不是枚舉now點的所有連邊,而是1~nfor(re int i=1;i<=n;++i){if(!vis[i]&&minn>dis[i]){minn=dis[i];now=i;}}ans+=minn;//枚舉now的所有連邊,更新dis數組for(re int i=head[now];i;i=e[i].next){re int v=e[i].v;if(dis[v]>e[i].w&&!vis[v]){dis[v]=e[i].w;}}}return ans; } int main() {init();printf("%d",prim());return 0; }總結
- 上一篇: 牛客网【每日一题】4月16日题目精讲 逆
- 下一篇: ST表讲解