網絡流
最大流
描述
在圖中,給出每條邊的最大流,求出從一給定源點到匯點的最大流
算法
單純的貪心法由於搜索順序會導致結果不同的問題並不能保證求出正確的答案,只需要將原始貪心算法中加入一個“反悔”機制就可以保證求出正確答案
實現中只需要加入反向邊,沒流過一條邊,就將這條邊的邊權減去流量,並把反向邊的邊權加上這個流量
實際上就是通過加入反向邊實現抵消操作
Ford-Fulkerson
dfs
時間複雜度:
Edmonds-Karp
bfs優化
時間複雜度:
Dinic
每次找一條路前,先通過一次bfs更新每個點的level,目的是在之後的dfs中優先遍歷深度淺的點
加入當前弧優化,避免對一條沒有用的便進行多次檢查
時間複雜度:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=10005,MAXM=100005,INF=~0U>>1;
int N,M,S,T;
struct E{int next,to,cap;} e[MAXM<<1];
int ecnt=1,G[MAXN];
void addEdge(int u,int v,int c)
{
e[++ecnt]=(E){G[u],v,c};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0};G[v]=ecnt;
}
int dfn[MAXN];
queue<int> que;
bool calDfn()
{
int i;
memset(dfn,-1,sizeof(dfn));
dfn[S]=0;que.push(S);
while(!que.empty())
{
int u=que.front();que.pop();
for(i=G[u];i;i=e[i].next)
if(e[i].cap>0&&dfn[e[i].to]<0)
{
int v=e[i].to;
dfn[v]=dfn[u]+1;
que.push(v);
}
}
return dfn[T]>-1;
}
int iter[MAXN];
int calF(int u,int f)
{
if(u==T) return f;
for(int & i=iter[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]>dfn[u])
{
int res=calF(v,min(f,e[i].cap));
if(res>0)
{
e[i].cap-=res,e[i^1].cap+=res;
return res;
}
}
}
return 0;
}
int maxFlow()
{
int i,flow=0;
while(calDfn())
{
for(i=1;i<=N;i++) iter[i]=G[i];
int f;
while((f=calF(S,INF))>0)
flow+=f;
}
return flow;
}
int main()
{
int i;
scanf("%d%d%d%d",&N,&M,&S,&T);
for(i=1;i<=M;i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
addEdge(u,v,w);
}
printf("%d",maxFlow());
}
[網絡流24題] 搭配飛行員 [COGS14]
題目描述
飛行大隊有若干個來自各地的駕駛員,專門駕駛一種型號的飛機,這種飛機每架有兩個駕駛員,需一個正駕駛員和一個副駕駛員。由於種種原因,例如相互配合的問題,有些駕駛員不能在同一架飛機上飛行,問如何搭配駕駛員才能使出航的飛機最多。
如圖,假設有10個駕駛員,如圖中的V1,V2,…,V10就代表達10個駕駛員,其中V1,V2,V3,V4,V5是正駕駛員,V6,V7,V8,V9,V10是副駕駛員。如果一個正駕駛員和一個副駕駛員可以同機飛行,就在代表他們兩個之間連一條線,兩個人不能同機飛行,就不連。例如V1和V7可以同機飛行,而V1和V8就不行。請搭配飛行員,使出航的飛機最多。注意:因爲駕駛工作分工嚴格,兩個正駕駛員或兩個副駕駛員都不能同機飛行.
輸入格式
輸入文件有若干行
第一行,兩個整數n與n1,表示共有n個飛行員(2<=n<=100),其中有n1名飛行員是正駕駛員.
下面有若干行,每行有2個數字a,b。表示正駕駛員a和副駕駛員b可以同機飛行。
注:正駕駛員的編號在前,即正駕駛員的編號小於副駕駛員的編號.
輸出格式
輸出文件有一行
第一行,1個整數,表示最大起飛的飛機數。
樣例
flyer.in
10 5
1 7
2 6
2 10
3 7
4 8
5 9
flyer.out
4
裸題, 加源匯,全賦一,最大流。
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXV=205,MAXE=MAXV*MAXV,INF=~0U>>1;
int n,n1,S,T;
struct E{int next,to,cap;} e[MAXE<<1];
int ecnt=1,G[MAXV];
void addEdge(int u,int v,int c)
{
e[++ecnt]=(E){G[u],v,c};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0};G[v]=ecnt;
}
int dfn[MAXV];
queue<int> que;
bool calDfn()
{
int i;
memset(dfn,-1,sizeof(dfn));
dfn[S]=0;que.push(S);
while(!que.empty())
{
int u=que.front();que.pop();
for(i=G[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==-1)
dfn[v]=dfn[u]+1,que.push(v);
}
}
return dfn[T]!=-1;
}
int iter[MAXV];
int calF(int u,int f)
{
if(u==T) return f;
for(int & i=iter[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==dfn[u]+1)
{
int res=calF(v,min(f,e[i].cap));
if(res>0)
{
e[i].cap-=res,e[i^1].cap+=res;
return res;
}
}
}
}
int maxFlow()
{
int i,res=0;
while(calDfn())
{
int f;for(i=1;i<=T;i++) iter[i]=G[i];
while((f=calF(S,INF))>0)
res+=f;
}
return res;
}
int main()
{
freopen("flyer.in","r",stdin);
freopen("flyer.out","w",stdout);
int i,a,b;
scanf("%d%d",&n,&n1);
S=n+1,T=n+2;
for(i=1;i<=n1;i++) addEdge(S,i,1);
for(i=n1+1;i<=n;i++) addEdge(i,T,1);
while(scanf("%d%d",&a,&b)!=EOF) addEdge(a,b,1);
printf("%d\n",maxFlow());
return 0;
}
[網絡流24題] 太空飛行計劃 [COGS727]
題目描述
W 教授正在爲國家航天中心計劃一系列的太空飛行。每次太空飛行可進行一系列商業性實驗而獲取利潤。現已確定了一個可供選擇的實驗集合E={E1,E2,…,Em},和進行這些實驗需要使用的全部儀器的集合I={ I1, I2,…,In }。實驗Ej 需要用到的儀器是I的子集Rj∈I。配置儀器Ik 的費用爲ck 美元。實驗Ej 的贊助商已同意爲該實驗結果支付pj 美元。W教授的任務是找出一個有效算法,確定在一次太空飛行中要進行哪些實驗並因此而配置哪些儀器才能使太空飛行的淨收益最大。這裏淨收益是指進行實驗所獲得的全部收入與配置儀器的全部費用的差額。
對於給定的實驗和儀器配置情況,編程找出淨收益最大的試驗計劃。
輸入格式
第1行有2個正整數m和n(m,n <= 100)。m是實驗數,n是儀器數。接下來的m行,每行是一個實驗的有關數據。第一個數贊助商同意支付該實驗的費用;接着是該實驗需要用到的若干儀器的編號。最後一行的n個數是配置每個儀器的費用。
輸出格式
第1行是實驗編號;第2行是儀器編號;最後一行是淨收益。
樣例
shuttle.in
2 3
10 1 2
25 2 3
5 6 7
shuttle.out
1 2
1 2 3
17
只要收入大於成本那麼實驗就可以做,故從源點每個實驗連一條 cap 爲收入的邊,從每個實驗儀器向匯點連一條 cap 爲費用的邊,這樣,跑出的最大流就是總的花費了。
答案要求輸出選擇的實驗和實驗儀器,那麼對於所有最後一次還能遍歷到的點,說明其可以選擇,則輸出。最後的淨利潤即爲總收益減去總花費。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXV=205,MAXE=4e4+205,INF=~0U>>1;
int m,n,S,T;
struct E{int next,to,cap;} e[MAXE<<1];int ecnt=1,G[MAXV];
void addEdge(int u,int v,int c)
{
e[++ecnt]=(E){G[u],v,c};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0};G[v]=ecnt;
}
int dfn[MAXV];
queue<int> que;
bool calDfn()
{
int i;
memset(dfn,-1,sizeof(dfn));
dfn[S]=0;que.push(S);
while(!que.empty())
{
int u=que.front();que.pop();
for(i=G[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==-1)
dfn[v]=dfn[u]+1,que.push(v);
}
}
return dfn[T]!=-1;
}
int iter[MAXV];
int calF(int u,int f)
{
int i;
if(u==T) return f;
for(int & i=iter[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==dfn[u]+1)
{
int res=calF(v,min(f,e[i].cap));
if(res>0)
{
e[i].cap-=res,e[i^1].cap+=res;
return res;
}
}
}
return 0;
}
int dinic()
{
int i,f,res=0;
while(calDfn())
{
for(i=1;i<=T;i++) iter[i]=G[i];
while((f=calF(S,INF))>0) res+=f;
}
return res;
}
int main()
{
freopen("shuttle.in","r",stdin);
freopen("shuttle.out","w",stdout);
int i,j,x,sum=0;
scanf("%d%d",&m,&n);
S=m+n+1,T=S+1;
for(i=1;i<=m;i++)
{
scanf("%d",&x);addEdge(S,i,x);sum+=x;
while(getchar()==' ')
scanf("%d",&x),addEdge(i,x+m,INF);
}
for(i=1;i<=n;i++) scanf("%d",&x),addEdge(i+m,T,x);
int ans=sum-dinic();
for(i=1;i<=m;i++) if(dfn[i]!=-1) printf("%d ",i);putchar('\n');
for(i=1;i<=n;i++) if(dfn[i+m]!=-1) printf("%d ",i);putchar('\n');
printf("%d\n",ans);
return 0;
}
最小割
最大流=最小割
最大流的變體
多個源點和匯點
增加一個超級源點s和一個超級匯點t,從s向每個源點加入一條容量無窮的邊,從每一個匯點向t加入一條容量無窮的邊
無向圖
加兩條邊即可
頂點上流量限制
把結點“割開”,所有入邊連到一點A上,所有出邊連到一點B上,從A向B連一條cap爲該結點流量限制的邊即可
費用流
每條邊上通過單位流量需要一定費用
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=5005,MAXM=50005,INF=~0U>>1;
int N,M,S,T;
int minCost,maxFlow;
struct E{int next,to,cap,cost;} e[MAXM<<1];
int ecnt=1,G[MAXN];
void addEdge(int u,int v,int c,int w)
{
e[++ecnt]=(E){G[u],v,c,w};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0,-w};G[v]=ecnt;
}
int pre[MAXN];//pre:最短路中來的邊
int dis[MAXN];bool inQ[MAXN];
queue<int> que;
bool SPFA()
{
int i;
for(i=1;i<=N;i++) dis[i]=INF;
memset(inQ,false,sizeof(inQ));
inQ[S]=true,dis[S]=0;que.push(S);
while(!que.empty())
{
int u=que.front();que.pop();
for(i=G[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dis[v]>dis[u]+e[i].cost)
{
dis[v]=dis[u]+e[i].cost;
pre[v]=i;
if(!inQ[v])
{
inQ[v]=true;
que.push(v);
}
}
}
inQ[u]=false;
}
return dis[T]!=INF;
}
void calc()
{
int f=INF,u;
for(u=T;u!=S;u=e[pre[u]^1].to) f=min(f,e[pre[u]].cap);
for(u=T;u!=S;u=e[pre[u]^1].to) e[pre[u]].cap-=f,e[pre[u]^1].cap+=f;
minCost+=f*dis[T];
maxFlow+=f;
}
int main()
{
int i;
scanf("%d%d%d%d",&N,&M,&S,&T);
for(i=1;i<=M;i++)
{
int u,v,c,w;scanf("%d%d%d%d",&u,&v,&c,&w);
addEdge(u,v,c,w);
}
while(SPFA()) calc();
printf("%d %d\n",maxFlow,minCost);
return 0;
}
[網絡流24題] 運輸問題 [COGS739]
時間限制:1 s 內存限制:128 MB
題目描述
對於給定的 m 個倉庫和 n 個零售商店間運送貨物的費用,計算最優運輸方案和最差運輸方案。
輸入格式
輸出格式
程序運行結束時,將計算出的最少運輸費用和最多運輸費用輸出到文件 tran.out 中。
tran.in
2 3
220 280
170 120 210
77 39 105
150 186 122
tran.out
48500
69140
對於所有數據:
裸題,只需從原點到倉庫連邊,商店到匯點連邊,前兩種邊帶 cap ,商店向匯點連帶費用的邊,然後跑費用流即可。
至於最大費用,把費用設爲負的跑費用流即可,注意不要用 Dijkstra 了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXV=205,MAXE=MAXV*(MAXV+2),INF=~0U>>1;
int n,m,S,T;
int minCost;
struct E{int next,to,cap,cost;};
struct CFS
{
E e[MAXE<<1];
int ecnt,G[MAXV];
void addEdge(int u,int v,int c,int w)
{
e[++ecnt]=(E){G[u],v,c,w};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0,-w};G[v]=ecnt;
}
CFS(){ecnt=1;}
} G[2];
int pre[MAXV];
bool inQ[MAXV];int dis[MAXV];queue<int> que;
bool SPFA(int flag)
{
CFS & rG=G[flag>0];
int i;
memset(inQ,false,sizeof(inQ));
for(i=1;i<=m+n+1;i++) dis[i]=INF;
dis[S]=0;que.push(S);inQ[S]=true;
while(!que.empty())
{
int u=que.front();que.pop();
inQ[u]=false;
for(i=rG.G[u];i;i=rG.e[i].next)
{
int v=rG.e[i].to;
if(rG.e[i].cap>0&&dis[v]>dis[u]+rG.e[i].cost*flag)
{
dis[v]=dis[u]+rG.e[i].cost*flag;
pre[v]=i;
if(!inQ[v]) inQ[v]=true,que.push(v);
}
}
}
return dis[T]!=INF;
}
void calc(int flag)
{
CFS & rG=G[flag>0];
int f=INF,u;
for(u=T;u!=S;u=rG.e[pre[u]^1].to)
f=min(f,rG.e[pre[u]].cap);
for(u=T;u!=S;u=rG.e[pre[u]^1].to)
rG.e[pre[u]].cap-=f,rG.e[pre[u]^1].cap+=f;
minCost+=f*dis[T];
}
int main()
{
freopen("tran.in","r",stdin);
freopen("tran.out","w",stdout);
int i,j,c,w;
scanf("%d%d",&m,&n);
S=0,T=n+m+1;
//
for(i=1;i<=m;i++)
scanf("%d",&c),G[1].addEdge(S,i,c,0);
for(i=1;i<=n;i++)
scanf("%d",&c),G[1].addEdge(i+m,T,c,0);
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
scanf("%d",&w),G[1].addEdge(i,j+m,INF,w);
G[0]=G[1];
//
minCost=0;
while(SPFA(1))
calc(1);
printf("%d\n",minCost);
minCost=0;
while(SPFA(-1))
calc(-1);
printf("%d\n",-minCost);
return 0;
}
有上下界的網絡流
無源匯可行流
流程:
- 添加附加源點 S ,附加匯點 T ,將所有上界與下界之差作爲 cap 值;
- 統計每個點的 nodeFlow ,入度爲正,出度爲負;
- 對於每個節點 u :
- 若 nodeFlow 爲正,從 S 到 u 建立一條 cap 爲 nodeFlow 的邊
- 若 nodeFlow 爲負,從 u 到 T 建立一條 cap 爲 -nodeFlow 的邊
- 從 S 到 T 跑最大流,對於每條邊,其可行流爲流量下界加上最大流中流過的流量。
模板題
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define min(a,b) (a<b?a:b)
using namespace std;
const int MAXN=205,MAXM=40010,INF=~0U>>1;
int n,m,S,T;
int lower[MAXM],nodeFlow[MAXN];//in:+ out:-
int ans[MAXM];
struct E{int next,to,cap,id;} e[MAXM<<1];
int ecnt=1,G[MAXN];
void addEdge(int u,int v,int c,int id)
{
e[++ecnt]=(E){G[u],v,c,id};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0,0};G[v]=ecnt;
}
int dfn[MAXN];
queue<int> que;
bool calDfn()
{
int i;
memset(dfn,-1,sizeof(dfn));
dfn[S]=0;que.push(S);
while(!que.empty())
{
int u=que.front();que.pop();
for(i=G[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==-1)
dfn[v]=dfn[u]+1,que.push(v);
}
}
return dfn[T]!=-1;
}
int iter[MAXN];
int calF(int u,int f)
{
if(u==T) return f;
for(int & i=iter[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==dfn[u]+1)
{
int res=calF(v,min(f,e[i].cap));
if(res>0)
{
e[i].cap-=res,e[i^1].cap+=res;
return res;
}
}
}
return 0;
}
void dinic()
{
while(calDfn())
{
for(int i=1;i<=T;i++) iter[i]=G[i];
while(calF(S,INF)>0);
}
}
int main()
{
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
int i,u,v,l,r;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d%d%d",&u,&v,&l,&r);
addEdge(u,v,r-l,i);
lower[i]=l,nodeFlow[u]-=l,nodeFlow[v]+=l;
}
S=n+1,T=n+2;
for(i=1;i<=n;i++)
{
if(nodeFlow[i]>0) addEdge(S,i,nodeFlow[i],0);
else if(nodeFlow[i]<0) addEdge(i,T,-nodeFlow[i],0);
}
dinic();
bool ok=true;
for(i=G[S];i;i=e[i].next)
if(e[i].cap>0) {ok=false;break;}
if(ok)
{
printf("YES\n");
for(i=2;i<=ecnt;i++)
ans[e[i].id]=e[i^1].cap;
for(i=1;i<=m;i++) printf("%d\n",ans[i]+lower[i]);
}else printf("NO\n");
return 0;
}
有源匯可行流
添加一條從 t 到 s 的 cap 爲 INF 的邊,按無源匯跑即可。
有源匯最大流
按照無源匯可行流建圖,添加一條從 t 到 s 的 cap 爲 INF 的邊,跑 S 到 T 的最大流,判斷是否滿流,若是,再跑一次 s 到 t 的最大流即爲答案。
模板題
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=405,MAXM=20005,INF=~0U>>1;
int n,m;
struct E{int next,to,cap;} e[MAXM<<1];
int ecnt=1,G[MAXN];
void addEdge(int u,int v,int c)
{
e[++ecnt]=(E){G[u],v,c};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0};G[v]=ecnt;
}
int nodeFlow[MAXN],flowSum=0;
queue<int> que;
int dfn[MAXN];
bool calDfn(int S,int T)
{
int i;
memset(dfn,-1,sizeof(dfn));
dfn[S]=0;que.push(S);
while(!que.empty())
{
int u=que.front();que.pop();
for(i=G[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==-1)
dfn[v]=dfn[u]+1,que.push(v);
}
}
return dfn[T]!=-1;
}
int iter[MAXN];
int calF(int u,int T,int f)
{
if(u==T) return f;
for(int & i=iter[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==dfn[u]+1)
{
int res=calF(v,T,min(e[i].cap,f));
if(res>0)
{
e[i].cap-=res,e[i^1].cap+=res;
return res;
}
}
}
return 0;
}
int dinic(int S,int T)
{
int i,f,res=0;
while(calDfn(S,T))
{
for(i=1;i<=n+2;i++) iter[i]=G[i];
while((f=calF(S,T,INF))>0) res+=f;
}
return res;
}
int main()
{
int i,s,t,S,T;
scanf("%d%d%d%d",&n,&m,&s,&t);
S=n+1,T=n+2;
for(i=1;i<=m;i++)
{
int u,v,l,r;scanf("%d%d%d%d",&u,&v,&l,&r);
addEdge(u,v,r-l);
nodeFlow[u]-=l,nodeFlow[v]+=l;
}
for(i=1;i<=n;i++)
{
if(nodeFlow[i]>0) addEdge(S,i,nodeFlow[i]),flowSum+=nodeFlow[i];
else if(nodeFlow[i]<0) addEdge(i,T,-nodeFlow[i]);
}
addEdge(t,s,INF);
int mustFlow=dinic(S,T);
if(mustFlow==flowSum) printf("%d\n",dinic(s,t));
else printf("please go home to sleep\n");
return 0;
}
有源匯最小流
按照無源匯可行流建圖,先跑 S 到 T 的最大流,再添加一條從 t 到 s 的 cap 爲 INF 的邊,在殘餘網絡上跑第二次 S 到 T 的最大流,判斷是否滿流,若是,第二次的流量即爲最小流。
模板題
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
typedef long long ll;
using namespace std;
const int MAXN=1e5+5,MAXM=5e5,INF=~0U>>1;
int n,m;
struct E{int next,to,cap;} e[MAXM<<1];
int ecnt=1,G[MAXN];
void addEdge(int u,int v,int c)
{
e[++ecnt]=(E){G[u],v,c};G[u]=ecnt;
e[++ecnt]=(E){G[v],u,0};G[v]=ecnt;
}
ll nodeFlow[MAXN];
int dfn[MAXN];
queue<int> que;
bool calDfn(int S,int T)
{
int i;
memset(dfn,-1,sizeof(dfn));
dfn[S]=0;que.push(S);
while(!que.empty())
{
int u=que.front();que.pop();
for(i=G[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==-1)
dfn[v]=dfn[u]+1,que.push(v);
}
}
return dfn[T]!=-1;
}
int iter[MAXN];
ll calF(int u,int T,ll f)
{
if(u==T) return f;
for(int & i=iter[u];i;i=e[i].next)
{
int v=e[i].to;
if(e[i].cap>0&&dfn[v]==dfn[u]+1)
{
ll res=calF(v,T,min(f,(ll)e[i].cap));
if(res>0)
{
e[i].cap-=res,e[i^1].cap+=res;
return res;
}
}
}
return 0;
}
ll dinic(int S,int T)
{
int i;ll f,res=0;
while(calDfn(S,T))
{
for(i=1;i<MAXN;i++) iter[i]=G[i];
while((f=calF(S,T,INF))>0) res+=f;
}
return res;
}
int main()
{
freopen("data.in","r",stdin);
int i,s,t,S,T;
scanf("%d%d%d%d",&n,&m,&s,&t);
S=n+1,T=n+2;
for(i=1;i<=m;i++)
{
int u,v,l,r;scanf("%d%d%d%d",&u,&v,&l,&r);
addEdge(u,v,r-l);
nodeFlow[u]-=l,nodeFlow[v]+=l;
}
ll flowSum=0;
for(i=1;i<=n;i++)
{
if(nodeFlow[i]>0) addEdge(S,i,nodeFlow[i]),flowSum+=nodeFlow[i];
else if(nodeFlow[i]<0) addEdge(i,T,-nodeFlow[i]);
}
ll f1=dinic(S,T);
addEdge(t,s,INF);
ll f2=dinic(S,T);
if(f1+f2==flowSum) printf("%lld\n",f2);
else printf("please go home to sleep\n");
return 0;
}