次小生成树
这里只写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
手写版以后再说吧