上下界可行流
|Word count:773|Reading time:3min|Post View:
题解
先考虑无源汇的情况。将每条边的权值都设为$l_i$,但是此时图中不满足每个点流出和流入相等的限制,所以对于每个点,如果它的流入大于流出,由超级源点$S$向其连一条 流入-流出 的边,表示流出还应增加多少;如果它的流出大于流入,由其向超级汇点$T$连一条 流出-流入 的边,表示流入还应增加多少,对于原来的每条边建成权值为$r_i-l_i$的边,表示最多可以让起点的流出,终点的流入增加多少。然后再跑最大流。如果每个点在调整后都能满足流入=流出的限制(显然$S$连出去的边的总权值等于连向$T$的边的总权值,所以限制等价于这些边都能流满),则存在可行流。
如果原图中有源汇,则为了让每个点仍保持流入等于流出的限制,可以先建一条由汇点到源点的上下界分别为$0$和$inf$的边,转换成无源汇的问题。(此时源点到汇点的最大流就等于这条边的流量)
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+5; const int inf = 0x7fffffff;
int Case; int n,m,S,T,cnt; int head[N],d[N]; int fin[N],fout[N],ide[N]; struct nd{ int ne,to,w; }e[N<<1]; struct Edge{ int x,y,l,r; }a[N];
void in(int x,int y,int w){ e[++cnt].to=y;e[cnt].ne=head[x];head[x]=cnt;e[cnt].w=w; e[++cnt].to=x;e[cnt].ne=head[y];head[y]=cnt;e[cnt].w=0; }
bool bfs(){ static queue<int>q; for(int i=1;i<=T;++i)d[i]=0; q.push(S);d[S]=1; while(!q.empty()){ int x=q.front();q.pop(); for(int i=head[x];i;i=e[i].ne){ int y=e[i].to; if(!d[y]&&e[i].w>0){ d[y]=d[x]+1; q.push(y); } } } return d[T]!=0; }
int dinic(int x=S,int mn=inf){ if(x==T||!mn)return mn; int flow=0; for(int i=head[x];i;i=e[i].ne){ int y=e[i].to,maxf; if(d[y]==d[x]+1&&(maxf=dinic(y,min(mn,e[i].w)))>0){ e[i].w-=maxf;e[i^1].w+=maxf; mn-=maxf;flow+=maxf; if(!mn)break; } } if(!flow)d[x]=0; return flow; }
int main(){ scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m); for(int i=1;i<=n+2;++i)head[i]=fin[i]=fout[i]=0; cnt=1; for(int i=1;i<=m;++i){ scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].l,&a[i].r); in(a[i].x,a[i].y,a[i].r-a[i].l); fin[a[i].y]+=a[i].l; fout[a[i].x]+=a[i].l; ide[i]=cnt; } S=n+1;T=n+2; int Maxflow=0; for(int i=1;i<=n;++i) if(fin[i]>=fout[i]){ in(S,i,fin[i]-fout[i]); Maxflow+=fin[i]-fout[i]; } else in(i,T,fout[i]-fin[i]); while(bfs())Maxflow-=dinic(); if(Maxflow!=0)puts("NO"); else{ puts("YES"); for(int i=1;i<=m;++i)printf("%d\n",a[i].l+e[ide[i]].w); } } }
|