• 最小瓶颈路


    定义:

    最小瓶颈路问题是指在一张无向图中,询问一个点对$(u,v)$,需要找出从$u$到$v$的一条简单路径,使路径上所有边中最大值最小。

    根据查询次数不同,最小瓶颈路问题可分为单次查询和多次查询。

    单次查询:

    例题:Luogu P1396 营救 题目链接

    题解一:

    根据“最大值最小”,不难想到二分答案。

    答案肯定处于所有边中最小值和最大值之间,因此我们二分答案,$check$的时候以二分值为基准进行$BFS/DFS$,不经过权值大于二分值的边,如果能搜到终点,则说明二分值过大;如果不能搜到终点,则说明二分值过小。

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=1e4+10,maxm=4e4+10,inf=0x7fffffff;
     4 int heade[maxn],ev[maxm],ew[maxm],nexte[maxm];
     5 int isvis[maxn];
     6 int n,m,s,t,tot=0,mid;
     7 void add_edge(int u,int v,int w){tot++;ev[tot]=v;ew[tot]=w;nexte[tot]=heade[u];heade[u]=tot;}
     8 bool dfs(int ui)
     9 {
    10     int i,vi,wi;bool flag=false;
    11     isvis[ui]=1;if(ui==t){return true;}
    12     for(i=heade[ui];~i;i=nexte[i])
    13     {
    14         vi=ev[i];wi=ew[i];
    15         if(isvis[vi]||wi>mid){continue;}
    16         flag|=dfs(vi);
    17     }
    18     return flag;
    19 }
    20 int main()
    21 {
    22     int i,j,u,v,w,l,r,ans;
    23     cin>>n>>m>>s>>t;l=inf;r=-inf;
    24     memset(heade,-1,sizeof(heade));
    25     for(i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);l=min(l,w);r=max(r,w);add_edge(u,v,w);add_edge(v,u,w);}
    26     while(l<=r)
    27     {
    28         mid=(l+r)>>1;memset(isvis,0,sizeof(isvis));
    29         if(dfs(s)){ans=mid;r=mid-1;}
    30         else{l=mid+1;}
    31     }
    32     cout<<ans;
    33     return 0;
    34 }
    View Code

    题解二:

    利用$Kruskal$的过程。将所有边升序排序,逐一枚举每条边尝试将边两端的点所在集合进行合并,如果合并之后$u$和$v$在同一个集合中,则说明此时$u$到$v$连通。由于边的枚举是由小到大,因此可以保证最后一次合并的边的权值就是答案。

    代码:

    多次查询:

    例题:Luogu T15193 【模板】最小瓶颈路 题目链接

    题目背景

    题目描述

    给定一张带权无向图,每次询问两个点,找出两点间的一条简单路径,使路径中权值最大的边权值最小。

    输入输出格式

    输入格式:

    第一行为三个数字$n,m,q$,分别表示无向图节点数,边数和询问次数。

    第二行至第$m+1$行为三个数字$u,v,w$,表示一条边。

    第$m+2$行至第$m+q+1$行为两个数字$u,v$,表示一次询问。

    输出格式:

    共$q$行,每行一个数字,表示询问结果。

    输入输出样例

    输入样例#1:
    4 8 4
    2 1 6
    2 2 4
    3 2 6
    1 4 5
    4 4 2
    2 4 6
    3 4 10
    1 2 9
    4 1
    3 1
    3 4
    1 4
    
    输出样例#1:
    5
    6
    6
    5
    

    说明

    对于30%的数据,$n,qleq 500$

    对于60%的数据,$n,qleq 2 imes 10^3,mleq 10^5$

    对于100%的数据,$n,qleq 10^5,mleq 2 imes 10^5$

    注意判断自环和重边。

    题解:

    根据单次查询中的题解二,可以证明该无向图最小生成树中u到v的路径一定是u到v的最小瓶颈路之一(因为最小瓶颈路很可能有多条)。因此,对这张无向图预先进行$MST$,然后就变成了对于每个询问$(u,v)$,回答$u$到$v$的路径上的权值最大值。这个可以用倍增$LCA$解决。

    具体思路是在$ST$表预处理时,维护一个从该节点到其$k$级父亲中经过的所有边的最大权值。在查询中仍然将$u$和$v$往上跳,同时维护路径中最大值,到其$LCA$结束。

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=1e5+10,maxm=2e5+10,maxj=17,inf=0x7fffffff;
     4 struct edge{int u,v,w;}e[maxm];
     5 int heade[maxn],ev[maxm],ew[maxm],nexte[maxm];
     6 int father[maxn],dep[maxn];
     7 int fa[maxn][maxj],maxv[maxn][maxj];
     8 int n,m,q,root,tot=0,num=0;
     9 void add_edge(int u,int v,int w){tot++;ev[tot]=v;ew[tot]=w;nexte[tot]=heade[u];heade[u]=tot;}
    10 int cmp(edge a,edge b){return a.w<b.w;}
    11 int find(int x){return father[x]<0?x:father[x]=find(father[x]);}
    12 void union_set(int u,int v,int w)
    13 {
    14     int x=find(u),y=find(v);
    15     if(x==y){return;}
    16     if(-father[x]>-father[y]){father[x]+=father[y];father[y]=x;}
    17     else{father[y]+=father[x];father[x]=y;}
    18     add_edge(u,v,w);add_edge(v,u,w);
    19     num++;
    20 }
    21 void dfs(int ui)
    22 {
    23     int i,vi,wi;
    24     for(i=heade[ui];~i;i=nexte[i])
    25     {
    26         vi=ev[i];wi=ew[i];if(vi==fa[ui][0]){continue;}
    27         fa[vi][0]=ui;maxv[vi][0]=wi;dep[vi]=dep[ui]+1;
    28         dfs(vi);
    29     }
    30 }
    31 void init()
    32 {
    33     int i,j;
    34     for(j=1;(1<<j)<=n;j++){for(i=1;i<=n;i++){if(fa[i][j-1]){fa[i][j]=fa[fa[i][j-1]][j-1];maxv[i][j]=max(maxv[i][j-1],maxv[fa[i][j-1]][j-1]);}}}
    35 }
    36 int query(int u,int v)
    37 {
    38     int i,j,t,tmp=-inf;
    39     if(dep[u]<dep[v]){swap(u,v);}
    40     t=(int)(log(dep[u])/log(2));
    41     for(j=t;j>=0;j--){if(dep[u]-(1<<j)>=dep[v]){tmp=max(tmp,maxv[u][j]);u=fa[u][j];}}
    42     if(u==v){return tmp;}
    43     for(j=t;j>=0;j--){if(fa[u][j]&&fa[u][j]!=fa[v][j]){tmp=max(tmp,max(maxv[u][j],maxv[v][j]));u=fa[u][j];v=fa[v][j];}}
    44     return max(tmp,max(maxv[u][0],maxv[v][0]));
    45 }
    46 int main()
    47 {
    48     int i,j,u,v,w;
    49     //freopen("data.in","r",stdin);
    50     //freopen("test.out","w",stdout);
    51     cin>>n>>m>>q;root=(1+n)>>1;
    52     memset(heade,-1,sizeof(heade));memset(father,-1,sizeof(father));
    53     for(i=1;i<=m;i++){scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);}
    54     sort(e+1,e+m+1,cmp);
    55     for(i=1;i<=m;i++){u=e[i].u;v=e[i].v;w=e[i].w;union_set(u,v,w);if(num>=n-1){break;}}
    56     dfs(root);init();
    57     for(i=1;i<=q;i++)
    58     {
    59         scanf("%d%d",&u,&v);
    60         printf("%d
    ",query(u,v));
    61     }
    62     return 0;
    63 }
    View Code

    最小瓶颈路应用:次小生成树

    具体问题是给定一张无向图,求出一棵生成树,其所有边的权值之和仅次于最小生成树的权值之和(如果有多个最小生成树的话次小生成树就是最小生成树)。

    思路:

    先求出这个图的$MST$,然后枚举所有不在$MST$中的边,尝试将其加入$MST$中,这样势必会构成一个环。于是我们需要在这个环中任意断开一条边(除了刚刚加入的边),使其依然成为一棵树。为了尽量使权值之和最小,我们需要断开树上加入的边的两端点之间权值最大的边,这样就将该问题化归到了最小瓶颈路问题。

  • 相关阅读:
    jQuery 复选框全选反选
    Linux安装JDK详细步骤
    C++Primer>#include<iostream>与#include<iostream.h>以及#inclue<string> 和 #include<string.h>的区别
    OpenCV常见的几种背景消除的方法
    无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用
    #include<iostream>与#include<iostream.h>以及#inclue<string> 和 #include<string.h>的区别
    CEdit控件输入数字限制(转)
    将自己的博客转移,优化
    Window Style
    this指针基础介绍
  • 原文地址:https://www.cnblogs.com/XSC637/p/7663056.html
Copyright © 2020-2023  润新知