图论 —— 最小生成树 —— 最小瓶颈路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011815404/article/details/89456617

【概述】

最小瓶颈路是指:在一张无向图上,对于点 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;
}

猜你喜欢

转载自blog.csdn.net/u011815404/article/details/89456617