最小生成树

最小生成树

我们定义无向连通图的 最小生成树(Minimum Spanning Tree,MST)为边权和最小的生成树。

一个有N个点的图,边一定是大于等于N-1条的。图的最小生成树,就是在这些边中选择N-1条出来,连接所有的N个点。这N-1条边的边权之和是所有方案中最小的。

注意:只有连通图才有生成树,而对于非连通图,只存在生成森林。

如果一个无向连通图不包含回路(连通图中不存在环),那么就是一个树。

prim (普里姆算法)

核心:从已知扩散寻找最小。

实现步骤

Prim算法的思想

  1. 我们从图的顶点集合中任意选择的一个单项点,作为序列中的初始子树。
  2. 每一次迭代时,以一种贪心的思想来扩张当前的生成树,即把不在树的最近顶点添加到树中(我们所说的最近顶点,是指一个不在树中的顶点,它以一条边权最小的边和树中的顶点相连,而树的形状是无所谓的)。
  3. 当图的所有顶点都包含在所构造的树中以后,该算法就停止了。

image

当然,要注意的是最小生成树并不唯一,甚至同一种算法生成的最小生成树都>可能有所不同,但是相同的是无论生成怎样的最小生成树:

  • 能够保证所有节点连通(能够满足要求和条件)
  • 能够保证所有路径之和最小(结果和目的相同)
  • 最小生成树不唯一,可能多样的

代码

void Prim(int s)
{
    meset(d,0×3f,sizeof(d));
    meset(vis,0,sizeof(vis));
    d[s]=0;
    q.push(node(s,0));
    while(!q.empty())
    {
        int u=q.top().u;
        q.pop();
        if(vis[u])
        {
            continue;
        }
        mst+=d[u];
        vis[u]=1;
        for(int i=0;i<Graph[u].size();i++)
        {
            int v=Graph[u][i].v;
            int w=Graph[u][i].w;
            if(d[v]>w)
            {
                d[v]=w;
                q.push(node(v,w));
            }
        }
    }
}

Kruskal算法

步骤

步骤1:先对图中所有的边按照权值进行排序
步骤2:如果当前这条边的两个顶点不在一个连通块里面,那么咋就用并查集的Union函数把他们合并在一个连通块里面(也就是把他们放在最小生成树里面),如果再在一个并查集里面,我们就舍弃这条边,不需要这条边。
步骤3:一直执行步骤2,知道当边数等于定点数的数目减去1,那就说明这n个顶点就连合并在一个集合里面了;如果边数不等于顶点数目减去1,那么说明这些边就不连通。

image

代码

int father[M];
struct Edge
{
    int u,v,dis;//左右端点,权值
}ed[M];
bool cmp(Edge a,Edge b)
{
    return a.dis<b.dis;
}
void init(int n)//并查集的初始化
{
    for(int i=1;i<=n;i++)
        father[i]=i;
}
int find(int x)
{
    return father[x] == x ? x : father[x] = find(father[x]);
}
int Kruskal(int n,int m)
{
    int ans=0;//记入最小生成树的权值
    int num=0;//边的数目
    for(int i=0;i<m;i++)
    {
        int fu=find(ed[i].u);
        int fv=find(ed[i].v);
        if(fu!=fv)
        {
            father[fu]=fv;
            ans+=ed[i].dis;
            num++;
            if(num==n-1)break;
        }
    }
    if(num!=n-1) return -1;
    else return ans;
}
int main()
{
    int n,m;
    cin>>n>>m;
    init(n);
    for(int i=0;i<m;i++)
        cin>>ed[i].u>>ed[i].v>>ed[i].dis;
    sort(ed,ed+m,cmp);
    cout<<Kruskal(n,m);
    return 0;
}
posted @ 2025-07-07 15:39  星空丶star  阅读(23)  评论(0)    收藏  举报