版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Houheshuai/article/details/51447071
次小生成树模板题,次小生成树:第二棵最小生成树,和第一棵的边不全相同(开始时不知理解成什么玩意了)
先求一次最小生成树,将这棵树上的边标记
再判断去掉前面所求的最小生成树的某条边能否再求得一棵最小生成树
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 500 + 5;
const int maxm = 200000 + 5;
int fa[maxn];
int n, m;
struct Road //保存每条路的信息
{
int u, v, w, vis;
bool operator < (const Road& t) const { //按长度由小到大排序
return w < t.w;
}
} R[maxm];
//初始化并查集
void Init()
{
for (int i = 1; i <= n; i++) fa[i] = i; //每个点自成一个连通分量
}
//找x的祖先
int Find(int x)
{
return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
//判断图的连通性
bool isAccess()
{
int f = Find(1);
for (int i = 2; i <= n; i++) if (f != Find(i)) return false;
return true;
}
//kruskal求不要k这条边的最小生成树
int kruskal(int k)
{
int ans = 0;
Init();
for (int i = 0; i < m; i++)
{
if (i != k)
{
int tx = Find(R[i].u), ty = Find(R[i].v);
if (tx != ty)
{
ans += R[i].w;
fa[tx] = ty;
}
}
}
if (isAccess()) return ans; //如果是生成树,返回权值
return -1;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &R[i].u, &R[i].v, &R[i].w);
R[i].vis = 0;
}
sort(R, R + m); //将所有边排序
Init();
int ans = 0; //求最小生成树
for (int i = 0; i < m; i++)
{
int tx = Find(R[i].u), ty = Find(R[i].v);
if (tx != ty)
{
ans += R[i].w;
fa[tx] = ty;
R[i].vis = 1; //标记生成树中含有这条边
}
}
int ok = 0;
for (int i = 0; i < m; i++)
if (R[i].vis == 1) //看看不用这条边能否再次生成一棵最小生成树
{
if (kruskal(i) == ans) //生成了!
{
ok = 1;
break;
}
}
if (ok) printf("Yes\n");
else printf("No\n");
}
return 0;
}