上下界网络流
上下界网络流
NOI 前背板子...
-
记号约定
\(f(u,v)\) 表示 \(E(u,v)\) 的流量
\(c(u,v)\) 表示 \(E(u,v)\) 的流量上界
\(b(u,v)\) 表示 \(E(u,v)\) 的流量下届
-
要求
\(b(u,v)\le f(u,v)\le b(u,v)\)
\(\sum_if(u,i)=\sum_jf(j,u),u\not=s,t\)
无源汇上下界可行流
- 附加超级源点 \(ss\) 和超级汇点 \(tt\)
- 对于原图的边 \(E(u,v)\) ,其限制为 \([b,c]\) ,连边 \(u\rightarrow v\) ,容量 \(c-b\)
- 对于原图某点 \(u\) ,设 \(d(u)\) 为流入这个点所有边的下界之和减去流出中国点的所有边的下界之和。若 \(d(i)>0\) ,那么连边 \(ss\rightarrow i\) ,容量 \(d(i)\) ,若 \(d(i)<0\) ,那么连边 \(i\rightarrow tt\) ,容量为 \(-d(i)\)
- 跑完后若与 \(ss\) 相连的边全部满流(或与 \(tt\) 相连的边全部满流) ,即可以得到一个可行流
有源汇上下界可行流
- 在原图中加一条 \(t\rightarrow s\) ,容量 \([0,\infty]\) 的边,然后按照无源汇的建图求解即可
有源汇上下界最大流
- 建图与有源汇可行流一样
- 先跑一个可行流,然后拆掉 \(t\rightarrow s\) 的边,然后跑 \(s\) 到 \(t\) 的最大流,答案为 \(\sum_i f(s,i)\)
Code:
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using std::min;
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
//#define gc() (iT==iS?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
#define gc() getchar()
template <class T>
void read(T &x)
{
x=0;char c=gc();
while(!isdigit(c)) c=gc();
while(isdigit(c)) x=x*10+c-'0',c=gc();
}
//拆掉 t->s 的边,跑 s 到 t 的最大流,\sum_i f(s,i) 即为答案
const int N=210;
const int M=30010;
const int inf=0x3f3f3f3f;
int n,m,s,t,ss,tt;
int head[N],to[M],Next[M],edge[M],cnt=1;
void add(int u,int v,int w)
{
to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt;
to[++cnt]=u,edge[cnt]=0,Next[cnt]=head[v],head[v]=cnt;
}
int dep[N],ad[N],q[N],d[N],l,r;
bool bfs(int s,int t)
{
memset(dep,0,sizeof dep);
dep[q[l=r=1]=s]=1;
while(l<=r)
{
int now=q[l++];
for(int v,i=head[now];i;i=Next[i])
if(edge[i]&&!dep[v=to[i]])
{
dep[v]=dep[now]+1;
if((q[++r]=v)==t) return true;
}
}
return false;
}
int dfs(int now,int t,int flow)
{
if(now==t) return flow;
int res=flow,yuu;
for(int v,i=head[now];i&&res;i=Next[i])
if(edge[i]&&dep[v=to[i]]==dep[now]+1)
{
yuu=dfs(v,t,min(res,edge[i]));
if(!yuu){dep[v]=0;continue;}
edge[i]-=yuu,edge[i^1]+=yuu;
res-=yuu;
}
return flow-res;
}
int Dinic(int s,int t)
{
int maxflow=0,flow=0;
while(bfs(s,t)) while(flow=dfs(s,t,inf)) maxflow+=flow;
return maxflow;
}
int main()
{
read(n),read(m),read(s),read(t);
int sum=0;
for(int u,v,l,r,i=1;i<=m;i++)
{
read(u),read(v),read(l),read(r);
add(u,v,r-l);
d[u]-=l,d[v]+=l;
}
ss=n+1,tt=ss+1;
for(int i=1;i<=n;i++)
{
if(d[i]>0) add(ss,i,d[i]),sum+=d[i];
else add(i,tt,-d[i]);
}
add(t,s,inf);
if(sum!=Dinic(ss,tt)) puts("please go home to sleep");
else printf("%d\n",Dinic(s,t));
return 0;
}
有源汇上下界最小流
- 建图与无源汇可行流一样
- 先求 \(ss\rightarrow tt\) 的最大流,然后连边 \(t\rightarrow s\) 容量限制为 \([0,\infty]\) , 然后再求 \(ss\rightarrow tt\) 的最大流,答案即为 \(t\rightarrow s\) 的实际流量
Code:(没加当前弧被干掉了 5 分,不想加了)
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using std::min;
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
#define gc() (iT==iS?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
//#define gc() getchar()
template <class T>
void read(T &x)
{
x=0;char c=gc();
while(!isdigit(c)) c=gc();
while(isdigit(c)) x=x*10+c-'0',c=gc();
}
//先ss->tt,然后加边 t->s [0,inf] ,再跑一遍,答案 t->s 的流量
const int N=5e4+10;
const int M=4e5+10;
const int inf=0x3f3f3f3f;
int head[N],to[M],Next[M],edge[M],cnt=1;
void add(int u,int v,int w)
{
to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt;
to[++cnt]=u,edge[cnt]=0,Next[cnt]=head[v],head[v]=cnt;
}
int n,m,s,t,ss,tt,d[N],sum;
int q[N],l,r,dep[N];
bool bfs(int s,int t)
{
memset(dep,0,sizeof dep);
dep[q[l=r=1]=s]=1;
while(l<=r)
{
int now=q[l++];
for(int v,i=head[now];i;i=Next[i])
if(!dep[v=to[i]]&&edge[i])
{
dep[v]=dep[now]+1;
if((q[++r]=v)==t) return true;
}
}
return false;
}
int dfs(int now,int t,int flow)
{
if(now==t) return flow;
int res=flow,yuu;
for(int v,i=head[now];i&&res;i=Next[i])
if(dep[v=to[i]]==dep[now]+1&&edge[i])
{
yuu=dfs(v,t,edge[i]<res?edge[i]:res);
if(!yuu){dep[v]=0;continue;}
edge[i]-=yuu,edge[i^1]+=yuu,res-=yuu;
}
return flow-res;
}
int Dinic(int s,int t)
{
int ret=0,flow;
while(bfs(s,t))
while(flow=dfs(s,t,inf)) ret+=flow;
return ret;
}
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
read(n),read(m),read(s),read(t);
ss=n+1,tt=ss+1;
for(int u,v,l,r,i=1;i<=m;i++)
{
read(u),read(v),read(l),read(r);
add(u,v,r-l);
d[u]-=l,d[v]+=l;
}
for(int i=1;i<=n;i++)
{
if(d[i]>0) add(ss,i,d[i]),sum+=d[i];
else add(i,tt,-d[i]);
}
int flow=Dinic(ss,tt);
add(t,s,inf);
flow+=Dinic(ss,tt);
if(flow!=sum) puts("please go home to sleep");
else printf("%d\n",edge[cnt]);
return 0;
}
有源汇上下界最小费用流
- 建图和可行流一样
- 跑费用流即可
- 答案=跑出的费用+边下界 \(\times\) 边的费用