[USACO4.2]草地排水Drainage Ditches
阿新 • • 發佈:2018-12-06
傳送門
這個題幾乎可以用來作為最大流入門的題了,沒有任何掩飾,直接告訴你這是最大流。
這裡解釋一下網路流的建反向邊操作,這其實是一個退流操作,拿下面一個圖舉個例子:
如果最先走了中間那條邊,我們這個圖就會變成這樣:
此時我們發現那條反向邊能被另一條路徑經過,但是這樣不是錯的嗎,因為實際上這條路徑是不存在的,讓我們走走試試:
誒,這不就等價於上面那條路走掉了50流量,下面那條路也走掉了50流量,讓我們再仔細思考一下:中間那條邊的狀態又和最初一樣了,就好像沒有走過。
所以反向邊走掉多少流量就等於退回正向邊多少流量,然後我們就能發現這個做法顯然是正確的。
自我認為沒有什麼別的難理解的地方了。
程式碼(dinic):
#include<cstdio> #include<queue> #include<cstring> #define min(a,b) (a<b?a:b) int dis[201],ans,n,m,cnt=1,s,t,inf=1e9+7,pre[401],nxt[401],h[201],v[401];std::queue<int>q; void add(int x,int y,int z) { pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt,v[cnt]=z; pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt; } bool bfs() { memset(dis,0,sizeof dis); q.push(s),dis[s]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=h[x];i;i=nxt[i])if(!dis[pre[i]]&&v[i])dis[pre[i]]=dis[x]+1,q.push(pre[i]); } return dis[t]; } int dfs(int x,int flow) { if(x==t||!flow)return flow; int f=flow; for(int i=h[x];i;i=nxt[i]) if(v[i]&&dis[pre[i]]>dis[x]) { int y=dfs(pre[i],min(v[i],f)); f-=y,v[i]-=y,v[i^1]+=y; if(!f)return flow; } if(f==flow)dis[x]=-1;//這是一個優化,正確性顯然 return flow-f; } int main() { scanf("%d%d",&n,&m);s=1,t=m; for(int i=1,x,y,z;i<=n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z); for(;bfs();ans+=dfs(s,inf)); printf("%d\n",ans); }