码迷,mamicode.com
首页 > 其他好文 > 详细

次小生成树

时间:2020-07-05 23:29:40      阅读:97      评论:0      收藏:0      [点我收藏+]

标签:set   inf   fine   img   合并   show   最小生成树   一段   gif   

借鉴博客: https://www.cnblogs.com/Howe-Young/p/4911992.html

https://www.cnblogs.com/bianjunting/p/10829212.html

 

一,定义

权值第 2 小的生成树。

二,Prim 算法

① 算法思想

1,在最小生成树构建的过程中 ,在合并树的时候,将两棵树之间 除加进来 的最小边以外的其他边都尝试加入,

选择一条最小是能够替换两个最小树之间的边,然后不断更新,最后得到答案。

② 步骤

1.先求出来最小生成树。在求 最小生成树 的时候一并将 最小生成树任意两点之间路径当中的权值最大的那一条 求出来。

求这个有什么用呢?

原来 最小生成树加入一条边之后一定构成了回路,所以如果说 加入了边  ij , 此时 i 到 j 有两条路,这两条路构成一个回路,

要想构成生成树的话,必然删除这条回路上的边,要想和原来不一样且最小,则删除原先 最小生成树上 i 到 j 路径中的最大边

2.尝试添加最小生成树外的每一条边,删除上面找到的边。其中的权值最小的就是次小生成树。

③ 代码

技术图片
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define inf 0x3f3f3f3f
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define N 105
int a[N][N];    //邻接矩阵存图
int max[N][N];  //表示最小生成树中 i 到 j 的最大边权
int used[N][N]; //判断该边是否加入最小生成树
int p[N];      // 路径       
int dis[N];    // 集合 u 到 i 的最短距离
bool vis[N];   // 标记集合 u 的点
int n, m;       // m 是边数, n 是点数
void init()
{
    memset(vis, 0, sizeof(vis));
    memset(dis, 0x3f, sizeof(dis));
    memset(a, 0x3f, sizeof(a));
    memset(p, 0, sizeof(p));
    memset(used, 0, sizeof(used));
    memset(max, 0, sizeof(max));
}
int prim()  // 求 最小生成树
{
    int s = 1;
    dis[1] = 0, vis[s] = 1, p[s] = 0;
    int sum = 0;
    for (int i = 1; i < n; i++)   // 边数 为点数减一
    {
        for (int j = 1; j <= n; j++)  // 更新距离,标记起点
        {
            if (vis[j] == 0 && dis[j] > a[s][j])
            {
                dis[j] = a[s][j];
                p[j] = s;
            }
        }
        int min = inf;
        for (int j = 1; j <= n; j++)  // 找到 最小的那条边
        {
            if (vis[j] == 0 && dis[j] < min)
            {
                min = dis[j];
                s = j;
            }
        }
        if (min == inf)
            return -1;
        vis[s] = 1;
        sum += min;
        used[s][p[s]] = used[p[s]][s] = 1;

        for (int j = 1; j <= n; j++)   // 多了这一段
        {
            if (vis[j])
                max[j][s] = max[s][j] = MAX(max[j][p[s]], dis[s]);
        }
    }
    return sum;
}
int smst(int minT)  // 求 次小生成树
{
    int ans = inf;
    for (int i = 1; i <= n; i++)   //枚举最小生成树之外的边
        for (int j = i + 1; j <= n; j++)
            if (a[i][j] != inf && !used[i][j])
                ans = MIN(ans, minT + a[i][j] - max[i][j]);
    if (ans == inf)
        return -1;
    return ans;
}
void solve()
{
    int minT = prim();    // 最小生成树
    if (minT == -1)
    {
        puts("Not Unique!");
        return;
    }
    int secT = smst(minT); // 次小生成树
    if (secT == minT)
        printf("Not Unique!\n");
    else
        printf("%d\n",minT);
}

int main(void)
{
    int t; scanf("%d", &t);
    while (t--)
    {
        init();
        scanf("%d %d", &n, &m);
        for (int i = 0; i < m; i++)
        {
            int u, v, w; scanf("%d %d %d", &u, &v, &w);
            a[u][v] = a[v][u] = w;
        }
        solve();
    }
    system("pause");
    return 0;
}
View Code

 

次小生成树

标签:set   inf   fine   img   合并   show   最小生成树   一段   gif   

原文地址:https://www.cnblogs.com/asdfknjhu/p/13252284.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!