spring boot 呼叫關係
阿新 • • 發佈:2020-08-14
對於最小生成樹的定義:在一個有\(n\)個點的圖中,用\(n - 1\)條邊將其連成一棵樹,使得所有\(n - 1\)條邊的權值之和最小。
最小生成樹有兩種做法:Prim和Kruskal,本文章兩個都會介紹。
Prim:
前置知識:堆
Prim的做法就是把點分為已經加入最小生成樹的和未被加入的,每次把距離已加入的點最近的邊加入最小生成樹。不過記錄邊比較麻煩,我們可以記錄點,記\(v_i\)為節點\(i\)到已加入部分最短的邊的長度,而小根堆記錄\(v_i\)和\(i\):
- 首先隨便找一個點(一般選1號點)入小根堆。
- 每次取出堆頂\(u\)並pop。
- 判斷\(u\)是否已經加入最小生成樹
- 如果不是,將\(v_u\)
- 然後遍歷所有連線的點\(x\),
- 若\(v_x>v_u\),則將\(x\)加入堆。
- 重複2,3,4,5,6直到堆為空或者已經加入了\(n-1\)條邊
關於無法形成一個樹:該情況就是在結束後判斷\(n\)個點是否都已經被標記,或者由於記錄了邊的條數,也可以判斷邊數是否為1。
code:
#include<queue> #include<cstdio> #include<cstring> using namespace std; int n,m,cnt,ans; int v[5005]; bool f[5005];//f判斷該節點是否已經加入最小生成樹 struct node { int first,second; friend bool operator<(node x,node y){return x.first>y.first;} }; priority_queue<node>q;//小根堆 struct graph { int tot; int hd[5005]; int nxt[400005],to[400005],dt[400005]; void add(int u,int v,int w) { tot++; nxt[tot]=hd[u]; hd[u]=tot; to[tot]=v; dt[tot]=w; return ; } }g;//鏈式前向星存圖 int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); g.add(u,v,w); g.add(v,u,w); } memset(v,0x3f,sizeof(v)); q.push((node){0,1}); v[1]=0;//入堆第一個點 while(!q.empty()&&cnt<n-1)//cnt記錄邊的條數,若堆不為空或邊數不足n-1則繼續 { int xx=q.top().second;//取出堆頂 q.pop();//彈出 if(!f[xx])//若還未加入最小生成樹 { f[xx]=true;//標記為加入 cnt++;//邊數+1 ans+=v[xx];//記錄長度 for(int i=g.hd[xx];i;i=g.nxt[i])//遍歷能到達的點 if(v[g.to[i]]>g.dt[i])//滿足條件 { v[g.to[i]]=g.dt[i];//先更改v q.push((node){v[g.to[i]],g.to[i]});//然後入堆 } } } for(int i=1;i<=n;i++) if(!f[i]) { printf("orz"); return 0; }//判斷是否連通 printf("%d",ans); return 0; }
Kruskal:
前置知識:並查集
Kruskal的做法:
- 把所有邊按順序排序。
- 從第一條邊開始列舉。
- 如果邊的兩端聯通(用並查集判斷),就跳過。
- 否則就加入這條邊,併合並兩個端點的集合。
- 重複3,4步直到列舉完。
關於無法形成一個樹:判斷是否所有點都在同一個集合裡。
code:
#include<cstdio> #include<algorithm> using namespace std; int n,m,ans; struct node { int f,t,d; }a[200005];//存邊,Kruskal不用建圖 bool cmp(node x,node y){return x.d<y.d;} struct bin { int w[5005]; int find(int x) { if(x==w[x]) return x; w[x]=find(w[x]); return w[x]; } void add(int x,int y) { w[find(x)]=find(y); return ; } bool ask(int x,int y) { if(find(x)==find(y)) return true; else return false; } }b;//並查集 int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].f,&a[i].t,&a[i].d); for(int i=1;i<=n;i++) b.w[i]=i;//並查集初始化 sort(a+1,a+m+1,cmp);//按邊權排序 for(int i=1;i<=m;i++)//列舉每條邊 { if(b.ask(a[i].f,a[i].t)) continue;//連通則跳過 ans+=a[i].d;//否則記錄 b.add(a[i].f,a[i].t);//改為連通 } for(int i=2;i<=n;i++) if(!b.ask(1,n)) { printf("orz"); return 0; }//判斷是否連通 printf("%d",ans); return 0; }