题解 AcWing 2236. 伊基的故事 I - 道路重建
算法 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;
}
本文作者:ztx-,使用署名-非商业性使用 4.0 国际进行许可