定义:
对于一个无向图G(V, E),其定义了边权为W(u, v),若T为他的一颗最小生成树,那么我们假设存在一颗生成树T1,不存在任意一颗G的生成树T2满足W(T) <= W(T2) < W(T1)(此为非严格次小生成树),那么我们就称T1为G的次小生成树。
通俗易懂地来说可以理解为权值第二小的生成树。
解题思路:
这里只讨论非严格次小生成树。
如果想要得到次小生成树,可以通过替换最小生成树中的一条边做到。当一条边插入到最小生成树的时候,一定会形成一个环。只有将这个环里的最大的边去掉,才有可能得到次小生成树!(当然不能去掉那条刚加入进去的边)。把所有未在最小生成树里的边全部按照这个方法计算一次,就可以得到次小生成树了。
可以概括为以下几个步骤:
- 求最小生成树。(如果最小生成树都不存在,那一定不存在次小生成树)
- 向最小生成树中插入边。
- 去掉因为步骤2形成环中的最大边
难点无非就是步骤3,如何去找到形成的环中最大的边。
prim:
由于prim算法是集点算法,所以在求最小生成树的时候只需要去记录每两个点之间的最大边长。
关于如何去记录每两个点之间的最大边长
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define ll long long
#define MT(a,b) memset(a,b,sizeof(a))
const int maxn=1E5+5;
const int ONF=-0x3f3f3f3f;
const int INF=0x3f3f3f3f;
int n,m;
int mp[105][105];
bool vis[105][105];
int mst[105];
int lowcost[105];
int max_cost[105][105];
int prim(int start){
int ans=0,tmp,min_num;
for (int i=1;i<=n;i++){
lowcost[i]=mp[i][start];
mst[i]=start;
}
MT(vis,false);
MT(max_cost,0);
mst[start]=-1;
for (int i=1;i<n;i++){
min_num=INF;
tmp=-1;
for (int j=1;j<=n;j++){
if (min_num>lowcost[j]&&mst[j]!=-1){
min_num=lowcost[j];
tmp=j;
}
}
if (tmp==-1) return -1;
ans+=lowcost[tmp];
vis[tmp][mst[tmp]]=vis[mst[tmp]][tmp]= true;
mst[tmp]=-1;
for (int j=1;j<=n;j++){
if (mst[j]==-1&&j!=tmp) max_cost[j][tmp]=max_cost[tmp][j]=max(max_cost[j][mst[tmp]],lowcost[tmp]);
if (lowcost[j]>mp[j][tmp]&&mst[j]!=-1){
mst[j]=tmp;
lowcost[j]=mp[j][tmp];
}
}
}
return ans;
}
int second_mst(int MST){
int ans=INF;
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
if (mp[i][j]!=INF&&vis[i][j]== false)
ans=min(ans,MST-max_cost[i][j]+mp[i][j]);
return ans;
}
int main (){
int t;
scanf ("%d",&t);
while (t--){
int a,b,c;
MT(mp,INF);
scanf ("%d%d",&n,&m);
for (int i=1;i<=m;i++){
scanf ("%d%d%d",&a,&b,&c);
mp[a][b]=mp[b][a]=c;
}
int tmp1=prim(1);
int tmp2=second_mst(tmp1);
if (tmp1==tmp2) printf("Not Unique!\n");
else printf("%d\n",tmp1);
}
return 0;
}