版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
前言
关于次小生成树 详解及模板 仅kruskal (因为kru太好写了了了 后续再补上prim
其实九月份的时候就接触了最小生成树和最短路,迷迷糊糊过了一个月,现在回头看可以发现这两种算法有很多相似的地方/思想(举个栗子:prim和dijkstra \ 次短路和次小生成树)
思路
关于次小生成树,首先求出最小生成树,然后枚举每条不在最小生成树上的边(在原本的节点上添加一个vis属性进行判断即可),并把这条边放到最小生成树上面,然后就一定会形成环,那么我们在这条环路中取出一条(除了新加入的那一条边)最长的路(这里可以用d[u][v]来维护)。最终得到的权值就是次小生成树的权值。
实现
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx],d[manx][manx]; //d数组用来维护u到v的距离,在枚举边的时候会用到
vector<int>p[manx]; //扩展路径,path数组,缩写p
struct node{
int u,v,w;
bool vis; //比常规最小生成树多了vis属性判断边是否加入最小生成树的集合
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
int t;
cin>>t;
while(t--)
{
k=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){ //初始化操作,万物之父皆自己
p[i].clear();
p[i].push_back(i);
f[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
a[++k].u=u,a[k].v=v,a[k].w=w,a[i].vis=false;
}
sort(a+1,a+1+k,cmp);
ll ans=0;
int total=1;
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
ans+=a[i].w;
f[u]=v;
a[i].vis=1; //标记进去最小生成树的集合
total++;
int l1=p[u].size(),l2=p[v].size();
for(int j=0;j<l1;j++)
for(int k=0;k<l2;k++)
d[p[u][j]][p[v][k]]=d[p[v][k]][p[u][j]]=a[i].w; //记录最小生成树中扩展路径的距离
for(int j=0;j<l1;j++) p[v].push_back(p[u][j]); //合并路径
if(total==n) break;
}
ll res=inf; //次小生成树的集合
for(int i=1;i<=k;i++)
if(!a[i].vis) //枚举每条没加入最小生成树的集合
res=min(res,ans+a[i].w-d[a[i].u][a[i].v]);
//ans是最小生成树的权值,减去最小生成树中u到v这条边的权值,加上枚举的这一条边进行比较
if(res>ans) cout<<ans<<endl;
else cout<<"Not Unique!"<<endl;
}
return 0;
}