题目链接
洛谷 P3366 【模板】最小生成树
Prim 模板题
最小生成树:给定一个有 \(n\) 个点的无向图,从中选取 \(n-1\) 条边使图连通,同时满足这 \(n-1\) 条边权和最小,则这 \(n-1\) 条边就是这张图的最小生成树,其个边长度之和即为这 \(n-1\) 条边的边权和。
思路分析
观察到图中每个点都是最小生成树上的一个结点,考虑对这 \(n\) 个点进行分析。我们假设已经选出了一些点,每次往里增加一个点。因为要使边权和最小,所以我们应选择剩下点中与已经被选出的点相邻且距离最近的那个点,一步步向外拓展。
我们定义数组 \(d_i\) 表示第 \(i\) 个点到与其相邻的选出点的距离的最小值。那么每次选点中,我们需遍历 \(d\) 数组,找出未被选走的点中 \(d\) 值最小的哪个点 \(j\),把它选出来。然后更新那些与 \(j\) 直接连通的点的 \(d\) 值。若某一次选出来的 \(d_j=\inf\),则说明剩下的点与被选出来的点间不连通,无法选出最小生成树。
代码呈现
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;const int N=5010,inf=0x3f3f3f3f;
int n,m,ans;
int dis[N];
bool vis[N];
vector<pii> f[N];int main(){scanf("%d%d",&n,&m);while (m--){int u,v,w; scanf("%d%d%d",&u,&v,&w);f[u].push_back({v,w}),f[v].push_back({u,w});}memset(dis,0x3f,sizeof dis);dis[1]=0;for (int i=1;i<=n;++i){int u=0;for (int j=1;j<=n;++j){if (!vis[j] && dis[j]<dis[u]) u=j;}if (dis[u]==inf){ ans=-1;break; }ans+=dis[u],vis[u]=1;for (auto v:f[u]) dis[v.first]=min(dis[v.first],v.second);}if (ans==-1) printf("orz");else printf("%d",ans);return 0;
}
