次小生成树 【总结】

MST - 最小生成树


引子:给你N个点以及M条边,问你MST是否唯一,若不唯一输出-1,否则输出MST的值。


通过 枚举 + 删边 + 再求MST 完全可以做到求解上述题目,但时间复杂度过高。这里介绍一种(N*2)的算法,是结合prime算法实现的。


算法核心思想:在prime求MST的过程中 用数组存储MST里面任意两点间的唯一的路中 权值最大的那条边的权值。最后枚举不在MST里面的边,判断的权值 是否 和 MST里面 i 到 j 的最大权值相等,只要有一条边满足就可以说明MST不唯一。(因为我们可以用这条不在MST的边来 代替 在MST的边,这样MST肯定不唯一)



数组使用

prime 模版用到三个数组 low[] vis[] Map[][],不再详细解释。

MST[ i ][ j ] :  存储MST中i 到 j 的唯一的路中 权值最大的那条边的权值。

InMST[ i ][ j ] :  判断这条边是否在MST中.

pre[ i ] :  记录 i 点的前驱  fa ,low[ i ] = Map[ fa ][ i ]


对于选入MST的点next

MST更新:MST[ next ][ j ] = max(MST[ pre[ next ] ][ j ], low[ next ])。( j 属于MST里面的点)


代码实现:


#include 
#include 
#include 
#define MAXN 100+10
#define INF 100000000
using namespace std;
int Map[MAXN][MAXN];
bool vis[MAXN];
int low[MAXN];//上面三个数组 求MST 专用
int MST[MAXN][MAXN];//MST中 i 和 j的最大边权
int pre[MAXN];//记录前驱
bool InMST[MAXN][MAXN];//标记该边是否在MST中
int N, M;
void init()
{
    for(int i = 1; i <= N; i++)
    {
        Map[i][i] = 0;
        for(int j = 1; j < i; j++)
            Map[i][j] = Map[j][i] = INF;
    }
}
void getMap()
{
    int a, b, c;
    for(int i = 1; i <= M; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        if(Map[a][b] > c)
            Map[a][b] = Map[b][a] = c;
    }
}
void solve()
{
    //prime求MST
    int mincost = 0;//最小代价
    int next, Min;
    memset(InMST, false, sizeof(InMST));
    memset(MST, 0, sizeof(MST));
    for(int i = 1; i <= N; i++)
    {
        low[i] = Map[1][i];
        vis[i] = false;
        pre[i] = 1;//每个点前驱
    }
    vis[1] = true;
    for(int i = 2; i <= N; i++)
    {
        Min = INF; next = -1;
        for(int j = 1; j <= N; j++)
        {
            if(!vis[j] && Min > low[j])
            {
                next = j;
                Min = low[j];
            }
        }
        mincost += Min;
        vis[next] = true;
        int fa = pre[next];//当前找到点的 前驱
        InMST[next][fa] = InMST[fa][next] = true;//在MST中
        for(int j = 1; j <= N; j++)
        {
            if(vis[j] && j != next)//MST中的点
                MST[j][next] = MST[next][j] = max(MST[fa][j], low[next]);
            if(!vis[j] && low[j] > Map[next][j])//更新low
            {
                low[j] = Map[next][j];
                pre[j] = next;//更新前驱
            }
        }
    }
    //判断MST是否唯一
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j < i; j++)
        {
            if(Map[i][j] != INF && !InMST[i][j])//有边 且不在MST中
            {
                if(Map[i][j] == MST[i][j])
                {
                    printf("-1\n");
                    return ;
                }
            }
        }
    }
    printf("%d\n", mincost);
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        init();
        getMap();
        solve();
    }
    return 0;
}



测试数据:

3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2


输出:


3
-1


你可能感兴趣的:(最小,最大,次小生成树,算法与有趣代码--记录)