最小生成树-- 解析与模板

百度百科

1. 最小生成树之prim算法

算法简单描述

1).输入:一个加权连通图,其中顶点集合为V,边集合为E;

2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;

3).重复下列操作,直到Vnew = V:

a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);

b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;

4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。

慢慢想想就很好理解(尤其是学过离散数学)

就是一个点集,一个边集。 点集随便选一个点放进去, 边集置空。不断找和 点集 中的点 相连的权值最小的边的点(该点在点集外), 将该点放入点集, 该边放入边集。

直至这中放了n个点。

模板。

#define inf 0x3f3f3f
#define ms(x) memset(x, 0 ,sizeof(x))
const int N = 110;
int maze[N][N];
bool vis[N];
int lowc[N];
int Prim(int cost[][N], int n)
{
    int ans = 0;
    ms(vis);
    vis[0] = true;                             // 将 0 加入点集
    for(int i=1;i<n;i++) lowc[i] = cost[0][i]; //lowc[i] 记录终点未i的边的权值, 当前点集中只有0 所以找 cost[0][i]
    for(int i=1;i<n;i++)
    {
        int mic = inf;
        int p=-1;
        for(int j=0;j<n;j++)
            if(!vis[j] && mic>lowc[j])// 在点集之外寻找到点集里的点最短的边
            {
                mic = lowc[j];
                p = j;
            }
        if(mic == inf) return -1;     //如果无法找到更小的边 且未进行完n-1次, 即未取完n-1条边
        ans+=mic;                     //  将取到的边的权值加入答案中
        vis[p] = true;                // 加入点集
        for(int j=0;j<n;j++)
            if(!vis[j] && lowc[j] > cost[p][j])
            {
                lowc[j] = cost[p][j];    //看能否更新到点集中的点的边权
            }
    }
    return ans;
}

2.最小生成树之kruskal算法

1).记Graph中有v个顶点,e个边

2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边

3).将原图Graph中所有e个边按权值从小到大排序

4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中

                if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中

                                         添加这条边到图Graphnew


#define inf 0x3f3f3f3f
#define ms(x) memset(x, 0, sizeof(x))
#define mf(x) memset(x,-1, sizeof(x))
using namespace std;
const int N = 110;                          //点数
const int M = 100000;                       //边数
int f[N];
struct Edge{                               
    int u, v, w;
}edge[M];
int tol;
void addedge(int u, int v, int w)           //加边
{
    edge[tol].u = u;
    edge[tol].v = v;
    edge[tol++].w = w;
}
bool cmp(Edge a, Edge b){                   //边权从小到大排列。
    return a.w < b.w;
}
int Find(int x)                             //用并查集判断联通分量
{
    if(f[x] == -1) return x;
    else return f[x] = Find(f[x]);
}
int Kruskal(int n)
{
    mf(f);
    sort(edge, edge+tol, cmp);              //根据边权排序,这样就默认从最小的边开始选,如果边的两点不在同一个联通分量中
    int cnt = 0;                            //即未被加到生成树上, 那么就把它俩合并。
    int ans = 0;
    for(int i=0;i<tol;i++)
    {
        int u = edge[i].u;
        int v = edge[i].v;
        int w = edge[i].w;
        int t1 = Find(u);
        int t2 = Find(v);
        if(t1 != t2)
        {
            ans += w;
            f[t1] = t2;
            cnt++;
        }
        if(cnt == n-1) break;
    }
    if(cnt<n-1) return -1;
    else return ans;
}
int main()
{
    int n, m;
    while(cin>>n)
    {
        tol = 0;
        scanf("%d", &m);
        while(m--)
        {
            int u, v, w;
            scanf("%d%d%d",&u, &v, &w);
            addedge(u, v, w);
        }
        int ans = Kruskal(n);
        printf("%d\n",ans);
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值