基本思想:
假设连通网G=(V,E),令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。
在E中选择 代价最小 的边,若该边依附的顶点分别在T中不同的连通分量上,则将此边加入到T中;否则,舍去此边而选择下一条代价最小的边。依此类推,直至T中所有顶点构成一个连通分量为止
时间复杂度为O(eloge)(e为图中的边数),所以,适合于求 稀疏图 的最小生成树。
#include <bits/stdc++.h>
#define MAXN 200005
using namespace std;
int node,edge,ans=0,k=0;
int fat[MAXN],siz[MAXN];
struct EDGE
{
int from,to,cost;
} e[MAXN];
bool cmp(EDGE a,EDGE b) {return a.cost<b.cost;}
int Find(int x){ return (fat[x]==x)? x : fat[x]=Find(fat[x]); } //查 路径压缩
//要么是父节点,要么就递归找到子节点,并且记录下来,实现压缩。
//注意:路径压缩后会破坏原有的父子关系。
void unionn(int x,int y)//并
{
x=Find(x); y=Find(y);
if(siz[x]>siz[y]) swap(x,y);//扁平树
fat[x]=y; siz[y]+=siz[x];
}
bool kruskal()
{
sort(e+1,e+edge+1,cmp);
//贪心,排序
for(int i=1;i<=edge;++i)
{
if(k==node-1) break;
if(Find(e[i].from) != Find(e[i].to))
{
unionn(e[i].from,e[i].to);
ans+=e[i].cost; ++k;
}
}
return (k==node-1);
}
int main()
{
scanf("%d%d",&node,&edge);
//node点数,edge边数
for(int i=1;i<=edge;++i) scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].cost);
//邻接矩阵
for(int i=1;i<=node;++i) {fat[i]=i;siz[i]=1;}
//初始化 siz[i]=1 这样后面的累加有意义
if(kruskal()) printf("%d",ans);
return 0;
}
做题心得
1.往往题目中会出现“最少”,“最多”等含有“最”的字样。
2.可能会是最大生成树。即:在一开始贪心排序的时候改动。
3.图论的问题往往都涉及构图的策略(P1194)
4.涉及坐标注意精度 (P2872 )