題解 AcWing 2236. 伊基的故事 I - 道路重建
阿新 • • 發佈:2022-05-18
演算法 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; }