首先给出最小瓶颈树的定义:
树中的最大权值边 — 树中的最小权值边的值,是图G的各个生成树中最小的。
算法实现:
此类题要使用Kruskal算法进行求解,Kruskal算法详解:https://blog.csdn.net/luomingjun12315/article/details/47700237/
大体上是利用了Kruskal算法中的三条特性:
(1)、并查集的特性,如果祖先相同,则连接两点将会形成一个圈,破坏树形结构;
(2)、算法中对边枚举的方便性,通过不断对建造图所用边数的限制进行枚举;
(3)、易排序性(只是我单纯的这么感觉)。
知识点就这么多,下面给出两道非常经典易懂的例题(不要急,下面会给出总结):
题目链接: https://vjudge.net/problem/UVA-1395
#include<cstdio>
#include<map>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
#include<set>
#include<cmath>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAX_V=200+10;
const int MAX_M= MAX_V*(MAX_V-1)/2+10;
int par[MAX_V],a,b,qur,V,M;
struct edge
{
int u,v,cost;
bool operator <(const edge &s)
{
return cost<s.cost;
}
};
edge es[MAX_M];
int find(int x)
{
return par[x]==x?x:(par[x]=find(par[x]));
}
int krusical()
{
int res=INF;
for(int i=0;i<M;i++)
{
int min_l=es[i].cost,cnt=V,max_l=0;
for(int j=0;j<=V;j++) par[j]=j;
for(int j=i;j<M;j++)
{
int fu=find(es[j].u);
int fv=find(es[j].v);
int fcost=es[j].cost;
if(fu!=fv)
{
max_l=fcost;
par[fu]=fv;
cnt--;//生成树的边数=V-1;
}
}
if(cnt==1)
res=min(res,max_l-min_l);
}
if(res==INF) return -1;//判断是否构成图
return res;
}
int main()
{
while(scanf("%d %d",&V,&M)==2)
{
if(V==0&&M==0) break;
for(int i=0; i<M; i++) scanf("%d %d %d",&es[i].u,&es[i].v,&es[i].cost);
sort(es,es+M);
printf("%d\n",krusical());
}
return 0;
}
题目链接:https://vjudge.net/problem/UVA-10457
#include<cstdio>
#include<map>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
#include<set>
#include<cmath>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAX_V=200+10;
const int MAX_M=1000+10;
int par[MAX_V],a,b,qur,V,M;
struct edge
{
int u,v,cost;
bool operator <(const edge &s)
{
return cost<s.cost;
}
};
edge es[MAX_M];
int find(int x)
{
return par[x]==x?x:(par[x]=find(par[x]));
}
int krusical(int s,int t)
{
int res=INF;
for(int i=0;i<M;i++)
{
int max_l=es[i].cost;
for(int j=0;j<=V;j++) par[j]=j;
for(int j=i;j<M;j++)
{
int fu=find(es[j].u);
int fv=find(es[j].v);
int fcost=es[j].cost;
if(fu!=fv) par[fu]=fv;
if(find(s)==find(t))//说明已经连接为通路
{
//cout<<"输出过程之 "<<fcost<<" "<<max_l<<endl;
res=min(res,fcost-max_l);
//cout<<"输出中间值 "<<res<<endl;
break;
}
}
}
return res;
}
int main(){
while(scanf("%d %d",&V,&M)==2)
{
for(int i=0;i<M;i++) scanf("%d %d %d",&es[i].u,&es[i].v,&es[i].cost);
sort(es,es+M);
scanf("%d %d",&a,&b);
scanf("%d",&qur);
while(qur--)
{
int s,t; scanf("%d %d",&s,&t);
//cout<<"输出中间值 "<<endl;
printf("%d\n",a+b+krusical(s,t));
}
}
return 0;
}
简单总结一下,其实这两道题,题意都差不多,只是个别的细节上会有些差别,但是根据这两道题大致上可以归纳出最小瓶颈树的解题套路:
(1)、完整的Kruskal;
(2)、算法內的全部枚举;
至于结束条件,以及main函数中的写法,根据题意改改就OK;
以上为个人理解,有不对的欢迎在评论区留言~