次小生成树 克鲁斯卡尔
题目链接:https://cn.vjudge.net/contest/67265#problem/D
具体思路:这个题如果用prim的话,对于重边,如果用数组是存不了的,所以可以通过克鲁斯卡尔算法求次小生成树。
首先,求出最小生成树,在求最小生成树的过程中,记录下都有哪些边被记录到了最小生成树上,然后再试着求次小生成树。
每一次取最小生成树上的一条边,去除这条边,然后再跑一下克鲁斯卡尔算法,看一下这个时候的总的权值是多少,然后再取一个最小的,如果存在次小生成树的话,这就是次小生成树的值。
AC代码:
#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<stack>
#include<stdio.h>
#include<algorithm>
#include<map>
using namespace std;
# define inf 0x3f3f3f3f
# define maxn 200+100
# define ll long long
struct edge
{
int fr;
int to;
int cost;
} q[maxn*4];
int vis[maxn];
int father[maxn];
int n,m,ans;
int num;
int Find(int t)
{
return t==father[t]?t:father[t]=Find(father[t]);
}
bool cmp(edge t1,edge t2)
{
return t1.cost<t2.cost;
}
int krustra1()
{
int sum=0;
for(int i=1; i<=n; i++)
{
father[i]=i;
}
for(int i=1; i<=m; i++)
{
int t1=Find(q[i].fr);
int t2=Find(q[i].to);
if(t1!=t2)
{
sum+=q[i].cost;
father[t1]=t2;
vis[++ans]=i;//记录哪些边在最小生成树上。
}
}
if(ans<n-1)return -1;
return sum;
}
int krustra2(int t)
{
int sum=0;
for(int i=1; i<=n; i++)//记得每一次求的话,重新编号。
{
father[i]=i;
}
int y=0;
for(int i=1; i<=m; i++)
{
if(t==i)continue;
int t1=Find(q[i].fr);
int t2=Find(q[i].to);
if(t1!=t2)
{
sum+=q[i].cost;
father[t1]=t2;
y++;
}
}
if(y<n-1)return inf;//如果够不成最小生成树,那么就返回inf
return sum;
}
int main()
{
int T;
scanf("%d",&T);
int Case=0;
while(T--)
{
ans=0;
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&q[i].fr,&q[i].to,&q[i].cost);
}
sort(q+1,q+m+1,cmp);
int w=krustra1();
if(w==-1)printf("Case #%d : No way\n",++Case);
else
{
int temp=inf;
for(int i=1; i<=ans; i++)
{
temp=min(temp,krustra2(vis[i]));
}
if(temp==inf)printf("Case #%d : No second way\n",++Case);
else printf("Case #%d : %d\n",++Case,temp);
}
}
return 0;
}