最小生成树
假设 \(n\) 表示图中点数,\(m\) 表示图中边数。
Prim算法堆优化
时间复杂度 \(O(nlogn)\)。
核心思想:每次挑一条与当前集合相连的最短边。
code
int ans,cnt;
struct edge{int v,w;};
vector<edge> e[N];
int d[N], vis[N];
priority_queue<pair<int,int>> q;
bool prim(int s){
for(int i=0;i<=n;i++) d[i]=inf;
d[s]=0; q.push({0,s});
while(q.size()){
int u=q.top().second; q.pop();
if(vis[u])continue;//再出队跳过
vis[u]=1;//标记u已出队
ans+=d[u]; cnt++;
for(auto ed : e[u]){
int v=ed.v, w=ed.w;
if(d[v]>w){
d[v]=w;
q.push({-d[v],v});//大根堆
}
}
}
return cnt==n;
}
Kruskal算法
适用于稀疏图,时间复杂度 \(O(mlogm)\)。
核心思想:从小到大挑不多余的边。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N=200006;
int n, m;
int ans=0;
struct edge{
int u,v,w;
bool operator<(const edge &t)const
{return w < t.w;}
}e[N];//结构体存边
struct DSU {
vector<int> f, siz;
DSU() {}
DSU(int n) {
init(n);
}
void init(int n) {
f.resize(n);
std::iota(f.begin(), f.end(), 0);
siz.assign(n, 1);
}
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
}
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
bool kruskal(){//返回的是原图的连通性,最小生成树的答案并没有返回,注意自己处理。
sort(e+1,e+m+1);
DSU dsu(n+1);
int cnt=0;
for(int i=1; i<=m; i++){
int x=(e[i].u);
int y=(e[i].v);
if(!dsu.same(x,y))
{
dsu.merge(x,y);
ans+=e[i].w;
cnt++;
}
}
return cnt==n-1;
}
int main(){
cin>>n>>m;
for(int i=1; i<=m; i++)
cin>>e[i].u>>e[i].v>>e[i].w;
if(!kruskal()) puts("impossible");
else printf("%d\n",ans);
return 0;
}