注:本文算法使用链式前向星数据结构实现。学习链接:链式前向星-学习笔记
一、Prim算法
普通prim算法模板:
//用前向星录数据的时候记得把head初始化为-1 fill(dist,dist+LEN,MAX); memset(vis,0,sizeof vis); int ans=0; dist[1]=0; //如果题目要求输出最小生成树,就把题目要求的源点s的dist设为0 while(1){ //如果题目要求判断最小生成树是否能覆盖所有边,这个循环条件应该是i=n;while(n--)循环n次。 int u=-1,d=MAX; for(i=1;i<=N;i++){ if(!vis[i] && dist[i]<d){ u=i; d=dist[i]; } } if(u<0) break; //如果题目要求判断最小生成树是否能覆盖所有边,出现这样的情况说明不能覆盖所有边。 vis[u]=1; ans+=dist[u]; for(i=head[u];~i;i=mp[i].next){ //用前向星遍历u点所有的后继。i是各个后继点在mp的下标,mp[to]是u的各个后继点 int to=mp[i].to; if(!vis[to] && mp[i].w<dist[to]){//如果这个点没有被访问过、并且u->v的路径比点集S到v的路径要短,则更新。 dist[to]=mp[i].w; } } } O("%d ",ans);
堆优化的prim算法:
堆结构:
struct cmp{ bool operator () (int a,int b){ return dist[a]>dist[b]; } }; priority_queue<int,vector<int>,cmp> pq;
算法代码:
int ans=0; dist[1]=0; pq.push(1); while(!pq.empty()){ int u=pq.top(); pq.pop(); if(vis[u]) continue; vis[u]=1; ans+=dist[u]; for(i=head[u];~i;i=mp[i].next){ int to=mp[i].to; if(!vis[to] && mp[i].w<dist[to]){ dist[to]=mp[i].w; pq.push(to); } } } O("%d ",ans);
二、Kruskal算法
1.建立边表数据结构
typedef struct edge{ int u,v,w; edge(int u=0,int v=0,int w=0):u(u),v(v),w(w) { } bool operator < (const edge& obj) const { return w<obj.w; } }edge; edge mp[LEN*LEN];
2.编写并查集模板(以下代码没有写合并的Union操作。这个操作在主代码执行的时候已经实现)
int fa[LEN]; int init(){ int i; FF(i,LEN) fa[i]=i; } int findFa(int x){ if(x==fa[x]) return x; int r=x; while(r!=fa[r]){ r=fa[r]; } int t=x; while(x!=fa[x]){ t=fa[x]; fa[x]=r; x=t; } return r; }
3.编写主代码
sort(mp,mp+cnt); FF(i,cnt){ int fa_u=findFa(mp[i].u); int fa_v=findFa(mp[i].v); if(fa_u!=fa_v){ ans+=mp[i].w; fa[fa_u]=fa_v; edge_cnt++; if(edge_cnt>=N-1) break; } } O("%d ",ans);
注意:
①边表的范围要开大,因为边的数目可能是顶点数目的平方(准确说,有向图边树E=N*(N-1) )
②Prim算法在录边的数据的时候,因为是无向图,一条边要录成两条。Kruskal就没有这种必要了。
③各种初始化代码(比如并查集的init() )要注意加上。
打个OJ测试一下吧!
OJ链接:还是畅通工程
AC代码:
#include <stdio.h> #include <memory.h> #include <math.h> #include <string> #include <vector> #include <set> #include <stack> #include <queue> #include <algorithm> #include <map> #define I scanf #define OL puts #define O printf #define F(a,b,c) for(a=b;a<c;a++) #define FF(a,b) for(a=0;a<b;a++) #define FG(a,b) for(a=b-1;a>=0;a--) #define LEN 1010 #define MAX (1<<30)-1 #define V vector<int> using namespace std; int N; int fa[LEN]; int init(){ int i; FF(i,LEN) fa[i]=i; } int findFa(int x){ if(x==fa[x]) return x; int r=x; while(r!=fa[r]){ r=fa[r]; } int t=x; while(x!=fa[x]){ t=fa[x]; fa[x]=r; x=t; } return r; } typedef struct edge{ int u,v,w; edge(int u=0,int v=0,int w=0):u(u),v(v),w(w) { } bool operator < (const edge& obj) const { return w<obj.w; } }edge; edge mp[LEN*LEN]; int cnt=0; int main(){ // freopen("还是畅通工程.txt","r",stdin); int i,j,u,v,w; while(scanf("%d",&N),N){ init(); cnt=0; int ans=0; int edge_cnt=0; i=(N*(N-1))/2; while(i--){ I("%d%d%d",&u,&v,&w); mp[cnt++]=edge(u,v,w); // mp[cnt++]=edge(v,u,w); } sort(mp,mp+cnt); FF(i,cnt){ int fa_u=findFa(mp[i].u); int fa_v=findFa(mp[i].v); if(fa_u!=fa_v){ ans+=mp[i].w; fa[fa_u]=fa_v; edge_cnt++; if(edge_cnt>=N-1) break; } } O("%d ",ans); } return 0; }