文章目录
最小生成树
给定一个无向图,如果它的某个子图中任意两个顶点都互相连通且是一棵树,那么这棵树就是生成树 其中,边权和最小的生成树就是最小生成树(MST)
一些性质:
n-1条边
若T1,T2都是最小生成树,则T1,T2的各边权是相同的(可能连接的边不同),换句话说,若边权各不相同,则最小生成树唯一(实际上也是拟阵贪心的性质)
往生成树上加一条边就形成一个环,假设是(u,v)加边,那么就形成(u,…,lca(u,v),…,v,u)这个环
基本算法
Prim
与Dijkstra比较像 Dijkstra是什么(大雾
我们先在某一个集合中搞一个点
定义d[i]表示点i到这个集合的最小距离
然后不断往这个集合里添加离这个集合最近的点,直到所有的点都加入了这个集合。
每一次加入一个点 都要去搞一下这个点的相邻的点 来更新d[i]
感觉一下正确性显然(什么鬼操作
证明 略~~(其实讨厌这个字~~
详见白书106页
int prim()
{
d[0]=0;
int ans=0;
while(1)
{
int u=-1;
for(int v=0;v<=n;v++)
if(!vis[v]&&(u==-1||d[v]<d[u]))
u=v;
if(u==-1) break;
vis[u]=1;
ans+=d[u];
for(int v=0;v<=n;v++)
d[v]=min(d[v],c[u][v]);
}
return ans;
}
同Dijkstra一样,Prim也可以用堆优化 mark一下
但是我感觉优化效果不明显(可能要在一些特殊数据下或者数据较大的时候吧
Kruskal
按照边的权值从小到大依次check一下,如果不产生环,就把这条边加进去,直到联通所有的点(根据树的性质,则为有n-1条边时
判断是否产生环,主要是看这条边的两个端点是否已经连通
就需要并查集来维护
void Init()
{
for(int i=1;i<=n;i++)
f[i]=i;
}
int Find(int x)
{
if(f[x]!=x)
return f[x]=Find(f[x]);
return f[x];
}
bool Union(int x,int y)
{
int u=Find(x),v=Find(y);
if(u==v) return 0;
if(u>v) f[u]=v;
else f[v]=u;
return 1;
}
int Kruskal()
{
Init();
int num=0,ans=0;
for(int i=1;i<=m;i++)
if(Union(edge[i].u,edge[i]