图论 —— 生成树 —— 最小瓶颈路
【概述】
最小瓶颈路是指:在一张无向图上,对于点 u、v 找出从 u、v 的一条简单路径,使得路径上行所有边中最大值最小。
根据查询次数不同,最小瓶颈路问题可分为单次查询和多次查询。
【单次查询】
由于要求最大值最小,答案肯定处于所有边中最小值和最大值之间,那么进行二分在 check 的时候以二分值为基准进行 DFS,不经过权值大于二分值的边,如果能搜到终点,则说明二分值过大,如果不能搜到终点,则说明二分值过小。
struct Edge {
int to;
int dis;
} edge[N];
int head[N],next[N];
int vis[N];
int n,m,start,endd;
int tot,mid;
void add_edge(int u,int v,int w) {//添边
tot++;
edge[tot].to=v;
edge[tot].dis=w;
next[tot]=head[u];
head[u]=tot;
}
bool dfs(int x) {//dfs搜索
if(x==endd)
return true;
bool flag=false;
vis[x]=1;
for(int i=head[x]; i!=-1; i=next[i]) {
int y=edge[i].to;
int w=edge[i].dis;
if(vis[y]||w>mid) {
continue;
}
flag=flag|dfs(y);
}
return flag;
}
int main() {
cin>>n>>m;//n个点m条边
cin>>start>>endd;//从start到endd
int left=INF,right=-INF;
memset(head,-1,sizeof(head));
for(int i=1; i<=m; i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
left=min(left,w);
right=max(right,w);
add_edge(u,v,w);
add_edge(v,u,w);
}
int ans;
while(left<=right) {//二分答案
mid=(left+right)>>1;
memset(vis,0,sizeof(vis));
if(dfs(start)) {
ans=mid;
right=mid-1;
}
else {
left=mid+1;
}
}
cout<<ans<<endl;
return 0;
}
【多次查询】
由于最小瓶颈路可能有多条,那么无向图最小生成树中 u 到 v 的路径一定是 u 到 v 的最小瓶颈路之一。
首先对无向图求最小生成树 MST,然后对于每个询问 (u,v),利用倍增来回答 u 到 v 的路径上的权值最大值。
struct Edge {
int to;
int dis;
}edge[N];
int head[N],next[N];
int father[N],dep[N];
int G[1000][1000],maxx[1000][1000];
int n,m,q,root;
int tot,num;
void addEdge(int u,int v,int w) {
tot++;
edge[tot].to=v;
edge[tot].dis=w;
next[tot]=head[u];
head[u]=tot;
}
int Find(int x) {
return father[x]<0?x:father[x]=Find(father[x]);
}
void Union(int u,int v,int w) {
int x=Find(u),y=Find(v);
if(x==y)
return;
if(-father[x]>-father[y]) {
father[x]+=father[y];
father[y]=x;
}
else {
father[y]+=father[x];
father[x]=y;
}
addEdge(u,v,w);
addEdge(v,u,w);
num++;
}
void dfs(int x) {
for(int i=head[x]; i!=-1; i=next[i]) {
int y=edge[i].to;
int w=edge[i].dis;
if(y==G[x][0])
continue;
G[y][0]=x;
maxx[y][0]=w;
dep[y]=dep[x]+1;
dfs(y);
}
}
void init() {//对G进行初始化
for(int j=1; (1<<j)<=n; j++) {
for(int i=1; i<=n; i++) {
if(G[i][j-1]) {
G[i][j]=G[G[i][j-1]][j-1];
maxx[i][j]=max(maxx[i][j-1],maxx[G[i][j-1]][j-1]);
}
}
}
}
int query(int x,int y) {//查询从x到y的最小瓶颈路
if(dep[x]<dep[y])
swap(x,y);
int temp=-INF;
int t=(int)(log(dep[x])/log(2));
for(int j=t; j>=0; j--) {
if(dep[x]-(1<<j)>=dep[y]) {
temp=max(temp,maxx[x][j]);
x=G[x][j];
}
}
if(x==y)
return temp;
for(int j=t; j>=0; j--) {
if(G[x][j]&&G[x][j]!=G[y][j]) {
temp=max(temp,max(maxx[x][j],maxx[y][j]));
x=G[x][j];
y=G[y][j];
}
}
return max(temp,max(maxx[x][0],maxx[y][0]));
}
struct E{
int u,v;
int w;
bool operator <(const E &rhs)const{//按边权排序
return w<rhs.w;
}
}e[N];
int main() {
cin>>n>>m>>q;//n个点m条边q次查询
for(int i=1; i<=m; i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e+1,e+m+1);//按边权排序
memset(head,-1,sizeof(head));
memset(father,-1,sizeof(father));
for(int i=1; i<=m; i++) {//Kruskal求最小生成树
int u=e[i].u;
int v=e[i].v;
int w=e[i].w;
Union(u,v,w);
if(num>=n-1)
break;
}
root=(1+n)>>1;
dfs(root);//从根节点开始搜索
init();
for(int i=1; i<=q; i++) {
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",query(u,v));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】