次小生成树

这里只写Kruskal版本(太蒟了qwq)

大致思路

首先求出最小生成树,我们枚举每条不在最小生成树上的边,并把这条边放到最小生成树上面,然后就一定会形成环,那么我们在这条环路中取出一条最长的路(除了新加入的那一条边)。最终我们得到的权值就是次小生成树的权值。

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#define INF 0x3f3f3f3f
 
using namespace std;
const int MAXN = 110;
int n,m,mapp[MAXN][MAXN];
bool vis[MAXN],connect[MAXN][MAXN];
int dis[MAXN],maxd[MAXN][MAXN],per[MAXN];
void Init()
{
    memset(mapp,INF,sizeof(mapp));
    memset(connect,false,sizeof(connect));
}
int prim()
{
    memset(maxd,0,sizeof(maxd));
    memset(vis,false,sizeof(vis));
    for(int i = 1;i <= n;i ++)
    {
        dis[i] = mapp[1][i];per[i] = 1;//首先父亲节点都是根节点
    }
    vis[1] = 1;
    dis[1] = 0;
    int res = 0;
    for(int i = 1;i < n;i ++)
    {
        int index = -1,temp  = INF;
        for(int j = 1;j <= n;j ++)
            if(!vis[j] && dis[j] < temp)
            {
                index = j;temp = dis[j];
            }
        if(index == -1) return res;
        vis[index] = 1;
        connect[index][per[index]] = false;connect[per[index]][index] = false;//这条边已经在最小生成树中,后面我们就不能添加这条边了
        res += temp;
        maxd[per[index]][index] =maxd[index][per[index]] =  temp;//更新点之间的最大值
        for(int j = 1;j <= n;j ++)
        {
            if(j != index && vis[j])//只是更新我们已经遍历过来的节点
            {
                maxd[index][j] = maxd[j][index] = max(maxd[j][per[index]],dis[index]);
            }
            if(!vis[j] && mapp[index][j] < dis[j])
            {
                dis[j] = mapp[index][j];
                per[j] = index;
            }
        }
    }
    return res;
}
int main()
{
    int T;
    scanf("%d\n",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        Init();
        int u,v,w;
        for(int i = 0;i < m;i ++)
        {
            scanf("%d%d%d",&u,&v,&w);
            mapp[u][v] = w;mapp[v][u]  = w;
            connect[u][v] = true;connect[v][u] = true;
        }
        int ans = prim();
        bool over = false;
        //如果有某条边没有被最小生成树使用,并且i~j的最大值大于图中得到最大值,那么就表示次小生成树存在
        //相反就不会存在
         for(int i = 1;!over && i <= n;i ++)
            for(int j = 1;j <= n;j ++)
            {
                if(connect[i][j] == false || mapp[i][j] == INF)
                    continue;
                if(mapp[i][j] == maxd[i][j])//当边长度相同是就是表示最小生成树相同
                {
                    over = 1;
                    break;
                }
            }
        if(over)
            printf("Not Unique!\n");
        else
            printf("%d\n",ans);
    }
     //如果我们需要求解次小生成树的权值时,我们就要把在最小生成树中没有用过的边,加上然后减去对应环中最大的路径
     return 0;
}

代码出自https://blog.csdn.net/li1615882553/article/details/80011884
手写版以后再说吧

posted @ 2020-10-09 10:57  When_C  阅读(95)  评论(0编辑  收藏  举报