『最小生成樹』prim & Kruskal
阿新 • • 發佈:2018-09-15
amp truct turn get out const 貪心 方法 稠密圖
Kruskal和prim都是求最小生成樹的方法,兩種方法都是按照貪心來做的。但是Kruskal是從邊的角度入手,prim則是從點的角度入手。prim運用類似Dijkstra的方法來求,Kruskal運用並查集來求。
在復雜度方面,兩種算法各有所長。
在稀疏圖中,枚舉邊的Kruskal效率更高,在稠密圖中,枚舉點的prim效率更高。
Kruskal算法
按照邊權從小到大排序,每次判斷當前邊兩邊的點是否連通,如果沒有連通,就加入這條邊。
連通性我們可以用並查集維護。
復雜度為\(O(m*log_m+m)\)
附上代碼
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<math.h> #include<queue> using namespace std; struct nod{ int f,t,w; }; nod edge[200005]; int fa[5005]; int n,m; inline int cmp(nod a,nod b){ return a.w<b.w; } inline int getfa(int a){ return fa[a]==a?a:fa[a]=getfa(fa[a]); } inline void unio(int a,int b){ fa[getfa(a)]=getfa(b); } int main(){ std::ios::sync_with_stdio(false); for(register int i=0;i<=5000;i++){ fa[i]=i; } cin>>n>>m; for(register int i=1;i<=m;i++){ cin>>edge[i].f>>edge[i].t>>edge[i].w; } sort(edge+1,edge+m+1,cmp); int ans=0; for(register int i=1;i<=m;i++){ if(getfa(edge[i].f)!=getfa(edge[i].t)){ unio(edge[i].f,edge[i].t); ans+=edge[i].w; } } cout<<ans<<endl; return 0; }
prim算法
prim算法的核心思想是枚舉到當前樹距離最近的點,然後加入到當前的樹中。每次找到最近的沒有在樹中的點,然後加入數中,更新最近距離。
復雜度為\(O(n^2)\),當然也可以優化。
附上代碼
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; const int maxn=5005; const int INF=0x3f3f3f3f; int n,m,cnt=1,ans=0; int map[maxn][maxn],dis[maxn],fr[maxn]; bool vis[maxn]; inline void prim(){ vis[1]=1,dis[1]=0,fr[1]=1; for(register int i=1;i<=n;i++)dis[i]=map[1][i]; for(register int i=1;i<=n;i++){ int mi=INF,v=-1; for(register int j=1;j<=n;j++) if(!vis[j]&&dis[j]<mi)mi=dis[j],v=j; if(v!=-1){ cnt++; vis[v]=1; ans+=dis[v]; for(register int j=1;j<=n;j++){ if(vis[j])continue; if(dis[j]>map[v][j]){ dis[j]=map[v][j]; fr[j]=v; } } } } } int main(){ memset(map,0x3f,sizeof(map)); scanf("%d%d",&n,&m); for(register int i=1,x,y,z;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); map[x][y]=map[y][x]=min(map[x][y],z); } prim(); cout<<ans<<endl; }
『最小生成樹』prim & Kruskal