题解 AcWing 2236. 伊基的故事 I - 道路重建

该博客探讨了如何优化最大流算法以查找关键边。首先介绍了暴力方法,即多次运行最大流算法,但其时间复杂度较高。然后提出了更优的策略,通过一次最大流计算得到残量网络,并在此基础上检查每条边是否为关键边,降低了时间复杂度。最终给出了Dinic算法实现的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

\(\tt{Acwing题库链接}\)

算法 1 (暴力)

先对原图跑一遍最大流,再依次增加每条边的容量后重新求最大流,如果增加某条边的容量后最大流变大了,说明它就是关键边。

这个智障东西的时间复杂度大概是 \(O(n^2 m^2)\) 的(如果使用 Dinic 求最大流),显然需要优化。

算法 2

真的需要跑 m 次最大流吗?

其实不需要。对原图跑一次最大流之后,我们其实已经获得了一些有用的信息————原图的残量网络。注意到这样一个性质,在原图上增加某条边的容量后最大流变大时,在残量网络上增加这条边的容量后应该会存在一条从源点到汇点的增广路。

所以我们可以想出这样一个做法:先对原图跑一次最大流,求出原图的残量网络;再依次增加残量网络上每条边的容量,判断增加容量后是否存在从源点到汇点的增广路,如果存在说明它就是关键边。

这样搞的时间复杂度应该是 \(O(n^2 m)\) 的(瓶颈变成了 Dinic),足够通过此题。

代码:

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <bitset>
using namespace std;
const int INF=0x3fffffff;
int n,m;
struct edge{
    int v,w,nxt,num;
    edge(){v=w=nxt=0;}
    edge(int _v,int _w,int _nxt){v=_v;w=_w;nxt=_nxt;}
};
struct graph{
    static const int MAXN=500,MAXM=5000;
    edge g[MAXM*2+5];
    int tot,head[MAXN+5];
    graph(){tot=1;}
    void insert_edge(int u,int v,int w,int num){
        g[++tot]=edge(v,w,head[u]);head[u]=tot;g[tot].num=num;
        g[++tot]=edge(u,0,head[v]);head[v]=tot;g[tot].num=-num;
    }
    int lev[MAXN+5],_head[MAXN+5];
    bool bfs(){
        for(int i=1;i<=n;i++)lev[i]=0;
        queue<int> q;
        q.push(1);lev[1]=1;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i;i=g[i].nxt){
                int v=g[i].v,w=g[i].w;
                if(!lev[v]&&w){
                    q.push(v);
                    lev[v]=lev[u]+1;
                }
            }
        }
        if(lev[n])return true;
        else return false;
    }
    int dfs(int u,int in){
        if(u==n)return in;
        int out=0;
        for(int &i=_head[u];i;i=g[i].nxt){
            int v=g[i].v,w=g[i].w;
            if(w&&lev[v]==lev[u]+1){
                int nxt=dfs(v,min(w,in));
                if(nxt){
                    in-=nxt;
                    out+=nxt;
                    g[i].w-=nxt;
                    g[i^1].w+=nxt;
                }
            }
        }
        if(out==0)lev[u]=INF;
        return out;
    }
    int Dinic(){
        int res=0;
        while(bfs()){
            for(int i=1;i<=n;i++)_head[i]=head[i];
            while(int tmp=dfs(1,INF)){
                res+=tmp;
            }
        }
        return res;
    }
};
graph g;
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;u++;v++;
        g.insert_edge(u,v,w,i);
    }
    g.Dinic();
    int ans=0;
    for(int i=1;i<=m;i++){
        g.g[(i<<1)].w++;
        if(g.bfs())ans++;
        g.g[(i<<1)].w--;
    }
    cout<<ans<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值