题目大意是这样的,给出n个点和m条边,每个边有一个流量的上下界,问是否存在一个循环的流量。这就是典型的无汇源点的上下界网络流,也就是循环流。做法是这样的,首先每条边我们都添加最少的流量,由于要满足流量守恒的条件,即每个点的流出量等于流入量,那么我们尝试进行修改,添加新的流量。我们记录一下每个点流出量与流入量的差,新建虚拟汇点源点,如果一个点流入量大于流出量,那么我们把他连向汇点,反之连向源点(容量都取绝对值),然后原来的m条边流量全部变为上下界的差。这样有源点有汇点的网络流就建出来了,跑一下最大流,如果最大流等于我们源点连出来的所有边的容量之和,那么就是有解的,反之就无解。因为我们新建的网络流源点与汇点的流量之和一定相同(原图上每个边的贡献为0,两两抵消),如果最大流满流意味着我们在每条边流量为下限的时候找到了一条新的流量使得原图的流量守恒,而我们取得是流量的下限,如果此时找不到流量守恒的流量,那么肯定就无解了。 —— by VANE
#include<cstdio> #include<queue> #include<cstring> using namespace std; const int N=300; const int M=N*400; int ss,tt,pre[M],other[M],last[N],l=1,cap[M],low[M]; int n,m,d[N],flow[M],dis[N]; void add(int a,int b,int c) { ++l;pre[l]=last[a];last[a]=l;other[l]=b;cap[l]=c;flow[l]=0; swap(a,b);++l;pre[l]=last[a];last[a]=l;other[l]=b;cap[l]=0;flow[l]=0; } bool bfs() { memset(dis,0,sizeof dis); queue<int> q; q.push(ss); dis[ss]=1; while(!q.empty()) { int u=q.front();q.pop(); for(int p=last[u];p;p=pre[p]) { int v=other[p]; if(!cap[p]||dis[v]) continue; dis[v]=dis[u]+1; q.push(v); } } if(dis[tt]) return 1; return 0; } int dfs(int x,int f) { if(x==tt) return f; if(f==0) return 0; int res=f; for(int p=last[x];p;p=pre[p]) { int v=other[p]; if(dis[v]!=dis[x]+1||!cap[p]) continue; if(!res) break; int tmp=dfs(v,min(cap[p],res)); res-=tmp; cap[p]-=tmp; flow[p]+=tmp; cap[p^1]+=tmp; flow[p^1]-=tmp; if(tmp==0) dis[v]=-1; } return f-res; } int dinic() { int res=0; while(bfs()) res+=dfs(ss,1e9); return res; } int main() { int T; scanf("%d",&T); while(T--) { l=1; memset(d,0,sizeof d); memset(last,0,sizeof last); scanf("%d%d",&n,&m); ss=n+1;tt=n+2; for(int i=1;i<=m;++i) { int a,b,c; scanf("%d%d%d%d",&a,&b,&low[i],&c); add(a,b,c-low[i]); d[a]+=low[i]; d[b]-=low[i]; } int tot=0; for(int i=1;i<=n;++i) { if(d[i]<0) add(ss,i,-d[i]); else add(i,tt,d[i]),tot+=d[i]; } if(dinic()!=tot) puts("NO"); else { puts("YES"); for(int i=1;i<=m;++i) printf("%d ",flow[i<<1]+low[i]); } } }