最小生成树
最小生成树问题是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图G中的边,并且满足整棵树的边权之和最小。两种经典的算法是Prim算法和Kruskal算法,这里给出两种算法我的实现:
Prim算法
核心在于加点,每次将还不在图中的,和图距离最小的点加到图里,直到所有点都被加到图中或所有边都已经用完退出。这里可以看到和dijkstra算法很相似,区别在于最短路算法是有确定的起点的,每次加的是到这个起点距离最小的点,Prim加的是到当前已经构造好的最小生成树距离最小的点。
(在这两个算法中我们很容易想到的数据结构是优先队列,因为这是一个动态变化的图)
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef pair<int,int> pii;
const int maxn=5005;
int n,m,u,v,w,cnt,len;
pii tmp;
bool flag;
ll sum;
bool vis[maxn]; // 记录点是否已经在图中
int dis[maxn][maxn]; // 距离矩阵
vector<int> tos[maxn];
struct cmp{
bool operator()(pii a,pii b){
return a.first>b.first;
}
};
priority_queue<pii,vector<pii>,cmp> pq;
int main(){
cin>>n>>m;
memset(dis,0x3f,sizeof dis);
while(m--){
cin>>u>>v>>w;
dis[u][v]=min(w,dis[u][v]);
dis[v][u]=dis[u][v];
tos[u].push_back(v);
tos[v].push_back(u);
}
// 任选一点作为起点,以1为例
cnt=1;
vis[1]=1;
len=tos[1].size();
for(int i=0;i<len;i++)
pq.push(pii(dis[1][tos[1][i]],tos[1][i]));
// 加完了所有点或已经用完了所有边退出
while(cnt!=n&&!pq.empty()){
tmp=pq.top();
v=tmp.second;
pq.pop();
if(!vis[v]){
cnt++;
vis[v]=1;
sum+=tmp.first;
len=tos[v].size();
for(int i=0;i<len;i++)
if(!vis[tos[v][i]])
pq.push(pii(dis[v][tos[v][i]],tos[v][i]));
}
}
if(cnt==n)cout<<sum<<endl;
else cout<<"orz\n";
return 0;
}
Boruvka 算法
初始将所有点都看成一棵树,每次加端点在不同连通分量中的最短的边,直到加了点数-1的边或所有边已经被用完,退出。
连通分量很容易想到并查集,这里不展开说了,代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef pair<int,int> pii;
const int maxn=5005;
typedef long long int ll;
int n,m,u,v,w,cnt;
int pre[maxn];
ll sum;
int findroot(int x){
int r=x,tmp;
while(r!=pre[r])
r=pre[r];
while(r!=pre[x]){
tmp=pre[x];
pre[x]=r;
x=tmp;
}
return r;
}
void join(int x,int y){
int fx=findroot(x),fy=findroot(y);
if(fx!=fy)
pre[fx]=fy;
}
struct edg{
int u,v,w;
};
edg tmp;
struct cmp{
bool operator()(edg e1,edg e2){
return e1.w>e2.w;
}
};
priority_queue<edg,vector<edg>,cmp> pq;
int main(){
cin>>n>>m;
while(m--){
cin>>tmp.u>>tmp.v>>tmp.w;
pq.push(tmp);
}
for(int i=1;i<=n;i++)
pre[i]=i;
while(cnt<n-1&&!pq.empty()){
tmp=pq.top();
pq.pop();
u=tmp.u;
v=tmp.v;
w=tmp.w;
if(findroot(u)!=findroot(v)){
cnt++;
sum+=w;
join(u,v);
}
}
if(cnt==n-1)cout<<sum<<endl;
else cout<<"orz\n";
return 0;
}