【LOJ115】无源汇有上下界可行流(模板题)
大致题意: 给你每条边的流量上下界,让你判断是否存在可行流。若有,则还需输出一个合法方案。
大致思路
首先,每条边既然有一个流量下界\(lower\),我们就强制它初始流量为\(lower\)。
而考虑到它还有一个流量上界\(upper\),其实这就等同于建一条初始流量为\(0\),而容量为\(upper-lower\)的边。
但考虑到流量平衡,因此我们可以考虑对于每个点用\(v_i\)记录下其流量的不平衡值,即对于一条边\(x->y\),我们将\(v_x\)减去\(lower\),将\(v_y\)加上\(lower\)。
在所有边加完后,对于所有\(v_i\)不等于\(0\)的点,如果\(v_i\)为正,则我们从源点向其连一条边权为\(v_i\)的边;如果\(v_i\)为负,则我们由其向汇点连一条边权为\(-v_i\)的边。
然后跑最大流。
如果能流满,就说明存在可行流,输出\(YES\),方案就是每条边当前流量加上其初始的下界\(lower\)。
否则,输出\(NO\)。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200
#define M 10200
#define INF 1e9
#define min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int n,m;
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
class UpperLowerFeasibleFlow_without_ST//无源汇有上下界可行流
{
private:
#define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].Cap=v)
#define AddOneWayEdge(x,y,v) (add(x,y,v),add(y,x,0))
static Con int Psz=N,Lsz=N+M<<1;int ee,iv[Lsz+5],lnk[Psz+5],cur[Psz+5],v[Psz+5],q[Psz+5],dep[Psz+5];
struct edge {int to,nxt,Cap;}e[Lsz+5];
I bool BFS()//BFS找增广路
{
RI i,k,H=1,T=1;memset(dep,0,sizeof(dep)),dep[q[1]=s]=1;W(H<=T&&!dep[t])
for(i=lnk[k=q[H++]];i;i=e[i].nxt) e[i].Cap&&!dep[e[i].to]&&(dep[q[++T]=e[i].to]=dep[k]+1);
return dep[t]?(memcpy(cur,lnk,sizeof(lnk)),true):false;
}
I int DFS(CI x,RI f)//DFS统计流量
{
if(!(x^t)||!f) return f;RI i,t,res=0;
for(i=cur[x];i;i=e[i].nxt)
{
if(cur[x]=i,(dep[x]+1)^dep[e[i].to]||!(t=DFS(e[i].to,min(f,e[i].Cap)))) continue;
if(e[i].Cap-=t,e[((i-1)^1)+1].Cap+=t,res+=t,!(f-=t)) break;
}return !res&&(dep[x]=-1),res;
}
public:
int s,t;I UpperLowerFeasibleFlow_without_ST() {s=1,t=2;}I int P(CI x) {return x+2;}
I void Add(CI p,CI x,CI y,CI Lower,CI Upper) {iv[p]=Lower,AddOneWayEdge(x,y,Upper-Lower),v[x]-=Lower,v[y]+=Lower;}
I void Build(CI n) {for(RI i=3;i<=n+2;++i) v[i]>0&&AddOneWayEdge(s,i,v[i]),v[i]<0&&AddOneWayEdge(i,t,-v[i]);}//对于所有v[i]不等于0的点进行连边操作
I void FeasibleFlow()//求出一个可行流
{
W(BFS()) DFS(s,INF);RI i;for(i=lnk[s];i;i=e[i].nxt) if(e[i].Cap) return (void)(puts("NO"));//如果没流满,输出NO
for(puts("YES"),i=1;i<=m;++i) printf("%d\n",iv[i]+e[i<<1].Cap);//输出这条边的下界lower与它的流量的和
}
}V;
int main()
{
RI i,x,y,Lower,Upper;for(F.read(n,m),i=1;i<=m;++i) F.read(x,y,Lower,Upper),V.Add(i,V.P(x),V.P(y),Lower,Upper);//读入+建边
return V.Build(n),V.FeasibleFlow(),0;//求答案
}
待到再迷茫时回头望,所有脚印会发出光芒