最小生成树
两种算法都是基于贪心的构造。主要思想都是类似于松弛的操作。找当前可以松弛的最小的边。
P3366 【模板】最小生成树
kruskal
这个是通过优先队列来做的。从小到大考虑每一条边然后看是否这条边的端点已经在一个连通块内了。具体实现用并查集。
#include<bits/stdc++.h>
using namespace std;
#define pii pair <int,int>
#define mp make_pair
const int N=2e6+7;
int fa[N],n,m;
struct node{int w,u,v;};
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
priority_queue <node> t;
bool operator < (const node &x,const node &y){return x.w>y.w;}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1,u,v,w;i<=m;i++)cin>>u>>v>>w,t.push({w,u,v});
int num=0,ans=0;
while(!t.empty()){
int u=t.top().u,v=t.top().v,w=t.top().w;t.pop();
u=find(u),v=find(v);if(u==v)continue;
fa[u]=v;ans+=w;num++;
}
if(num!=n-1){cout<<"orz";return 0;}
cout<<ans;return 0;
}
prim
这个东西则是维护连通块。维护当前连通块到其他点的最小距离然后贪心的去找。
#include<bits/stdc++.h>
using namespace std;
const int N=5005,inf=1e9+7;
int n,m,mp[N][N],dis[N],vis[N];
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)mp[i][j]=inf;
for(int i=1,u,v,w;i<=m;i++)cin>>u>>v>>w,mp[u][v]=mp[v][u]=min(mp[u][v],w);
for(int i=2;i<=n;i++)dis[i]=min(inf,mp[1][i]);dis[1]=0;
int num=0,u=1,val=inf,ans=0;
while(num+1<n){
vis[u]=1;val=inf;num++;
for(int i=1;i<=n;i++){if(!vis[i]&&val>dis[i])val=dis[i],u=i;}//cout<<"* "<<i<<' '<<dis[i]<<'\n';
ans+=val;//cout<<u<<' '<<val<<'\n';
if(val==inf){cout<<"orz";return 0;}
for(int i=1;i<=n;i++){
if(!vis[i])dis[i]=min(dis[i],mp[u][i]);
}
}
cout<<ans;return 0;
}
复杂度
如果愿意的话空间都可以做到线性。
时间的话 kruskal 是 \(O(m\log m)\) 的。prim 是 \(O(n^2+m)\) 的。可以明显看出图的稀疏与否是决定二者效率的决定性因素。
虽然一般都写 kruskal,但是在某些时候 prim 是更优的。比如最小乘积模型。

浙公网安备 33010602011771号