以下的内容仅是一些对上下界网络流的补充说明,基础概念都没有涉及,但是有一些注意点。
这里的三道题(无源汇上下界可行流,有源汇上下界最大流,有源汇上下界最小流)是采用(LOJ)的模板,题号是(115-117)。
(LOJ115) 无源汇有上下界可行流
题意:
给定一个无源汇的图,流量有上下界,问一个可行流。
知识点:
无源汇有上下界可行流
解法:
每个点的容量转化为上界(-)下界,并统计每个点的多余流量,新建源汇点(SS,TT)连那些多余的流量,然后跑最大流即可。如果那些源汇边上不满流,就没有可行流,否则在流量上加上该边的下界即可。
备注:
注意:
- 一个与意识上理解起来不同的地方,如果入流(>)出流,应该是(SS)向(i)连边,否则是(i)向(TT)连边。
- 跑完最大流后的图上,正边是残量网络,反边才是跑了的流量,用反边(+)下界才是答案(其实用上界(-)正边也对)。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=210,maxm=31000,inf=0x7fffffff;
int n,m,SS,TT,tot,head[maxn],dis[maxn],cur[maxn],d[maxn];
struct node
{
int nxt,to,w,rg;
}edge[maxm];
queue<int>q;
int read()
{
int x=0;
char c=getchar();
while (c<48||c>57)
c=getchar();
while (c>=48&&c<=57)
x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void add(int u,int v,int w,int rg=0)
{
edge[++tot]=(node){head[u],v,w,rg};
head[u]=tot;
}
void link(int u,int v,int w,int rg=0)
{
add(u,v,w,rg);
add(v,u,0,rg);
}
bool bfs()
{
memset(dis,0,sizeof(dis));
int i,u,v;
q.push(SS);
dis[SS]=1;
while (!q.empty())
{
u=q.front();
q.pop();
for (i=head[u];i;i=edge[i].nxt)
{
v=edge[i].to;
if (!dis[v]&&edge[i].w>0)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[TT];
}
int dfs(int u,int flow)
{
if (u==TT)
return flow;
int res=flow,v,tmp;
for (int &i=cur[u];i;i=edge[i].nxt)
{
v=edge[i].to;
if (dis[v]==dis[u]+1&&edge[i].w>0)
{
tmp=dfs(v,min(res,edge[i].w));
res-=tmp;
edge[i].w-=tmp;
edge[i^1].w+=tmp;
if (!res)
break;
}
}
return flow-res;
}
void dinic()
{
int i,tmp;
while (bfs())
{
for (i=1;i<=TT;i++)
cur[i]=head[i];
tmp=dfs(SS,inf);
if (!tmp)
break;
}
}
int main()
{
int i,u,v,b,c;
n=read(),m=read();
tot=1;
SS=n+1,TT=SS+1;
for (i=1;i<=m;i++)
{
u=read(),v=read(),b=read(),c=read();
link(u,v,c-b,b);
d[v]+=b;
d[u]-=b;
}
for (i=1;i<=n;i++)
if (d[i]!=0)
{
if (d[i]<0)
link(i,TT,-d[i]);
else
link(SS,i,d[i]);
}
dinic();
int fl=0;
for (i=head[SS];i;i=edge[i].nxt)
if (edge[i].w)
{
fl=1;
break;
}
if (!fl)
for (i=head[TT];i;i=edge[i].nxt)
if (edge[i^1].w)
{
fl=1;
break;
}
if (fl)
puts("NO");
else
{
puts("YES");
for (i=3;i<=2*m+1;i+=2)
printf("%d
",edge[i].w+edge[i].rg);
}
return 0;
}
(LOJ116) 有源汇上下界最大流
题意:
问有一个有源汇的图,流量有上下界,问最大流。
知识点:
有源汇上下界最大流
解法:
先判断可行性,还是加入(SS)和(TT)来判断,但是(S)和(T)处的流量不平衡,所以我们让(T)向(S)连一条边(INF),这样就肯定平衡了,然后跑(SS)到(TT)的最大流,判断是否可行,注意这条边的流量就是可行流的大小。然后删掉这条边,再跑(S)到(T)的最大流,这个最大流(+)可行流就是答案了。
备注:
注意是删掉那条(T-S)的边,不是删掉(T)和(S)或者是(TT)和(SS)的这几个点。还有一种方法是不删这条边,然后直接跑(S)到(T)的最大流,因为肯定不会选(T-S)这条边,但是反边一定选(为什么我还没懂),所以可行流就加上了。或者直接二分可行流的大小,然后赋值到那条(T-S)边上也可以(不太懂怎么实现)。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=210,maxm=20500,inf=0x7fffffff;
int n,m,S,T,SS,TT,tot,head[maxn],cur[maxn],dis[maxn],d[maxn];
struct node
{
int nxt,to,w;
}edge[maxm];
queue<int>q;
int read()
{
int x=0;
char c=getchar();
while (c<48||c>57)
c=getchar();
while (c>=48&&c<=57)
x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void add(int u,int v,int w)
{
edge[++tot]=(node){head[u],v,w};
head[u]=tot;
}
void lnk(int u,int v,int w)
{
add(u,v,w);
add(v,u,0);
}
bool bfs(int s,int t)
{
memset(dis,0,sizeof(dis));
int i,u,v;
q.push(s);
dis[s]=1;
while (!q.empty())
{
u=q.front();
q.pop();
for (i=head[u];i;i=edge[i].nxt)
{
v=edge[i].to;
if (!dis[v]&&edge[i].w>0)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[t];
}
int dfs(int u,int flow,int t)
{
if (u==t)
return flow;
int v,tmp,res=flow;
for (int &i=cur[u];i;i=edge[i].nxt)
{
v=edge[i].to;
if (dis[v]==dis[u]+1&&edge[i].w>0)
{
tmp=dfs(v,min(res,edge[i].w),t);
res-=tmp;
edge[i].w-=tmp;
edge[i^1].w+=tmp;
if (!res)
break;
}
}
return flow-res;
}
int dinic(int s,int t)
{
int i,ans=0,tmp;
while (bfs(s,t))
{
for (i=1;i<=TT;i++)
cur[i]=head[i];
tmp=dfs(s,inf,t);
if (!tmp)
break;
ans+=tmp;
}
return ans;
}
int main()
{
int i,u,v,b,c,maxflow=0;
n=read(),m=read(),S=read(),T=read();
SS=n+1,TT=SS+1;
tot=1;
for (i=1;i<=m;i++)
{
u=read(),v=read(),b=read(),c=read();
lnk(u,v,c-b);
d[u]+=b,d[v]-=b;
}
for (i=1;i<=n;i++)
if (d[i]!=0)
{
if (d[i]>0)
lnk(i,TT,d[i]),maxflow+=d[i];
else
lnk(SS,i,-d[i]);
}
lnk(T,S,inf);
maxflow-=dinic(SS,TT);
if (maxflow)
{
puts("please go home to sleep");
return 0;
}
maxflow=edge[tot].w;
edge[tot].w=edge[tot^1].w=0;
printf("%d
",maxflow+dinic(S,T));
return 0;
}
(LOJ117) 有源汇上下界最小流
题意:
问有一个有源汇的图,流量有上下界,问最小流。
知识点:
有源汇上下界最小流
解法:
前面连边同上下界最大流,考虑反边的最大减少量(=)正边的最小增加量,所以删边后可行流(-)(T)到(S)的最大流即为最小流。
备注:
最好和(0)取个(max)再输出,因为不知道最小流会不会(<0)不合法。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=50010,maxm=350020;
const ll inf=0x7fffffffffffffffll;
int n,m,S,T,SS,TT,tot,head[maxn],cur[maxn],dis[maxn];
ll d[maxn];
struct node
{
int nxt,to;
ll w;
}edge[maxm];
queue<int>q;
ll read()
{
ll x=0;
char c=getchar();
while (c<48||c>57)
c=getchar();
while (c>=48&&c<=57)
x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void add(int u,int v,ll w)
{
edge[++tot]=(node){head[u],v,w};
head[u]=tot;
}
void lnk(int u,int v,ll w)
{
add(u,v,w);
add(v,u,0);
}
bool bfs(int s,int t)
{
memset(dis,0,sizeof(dis));
int i,u,v;
q.push(s);
dis[s]=1;
while (!q.empty())
{
u=q.front();
q.pop();
for (i=head[u];i;i=edge[i].nxt)
{
v=edge[i].to;
if (!dis[v]&&edge[i].w>0)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
return dis[t];
}
ll dfs(int u,ll flow,int t)
{
if (u==t)
return flow;
int v;
ll tmp,res=flow;
for (int &i=cur[u];i;i=edge[i].nxt)
{
v=edge[i].to;
if (dis[v]==dis[u]+1&&edge[i].w>0)
{
tmp=dfs(v,min(res,edge[i].w),t);
res-=tmp;
edge[i].w-=tmp;
edge[i^1].w+=tmp;
if (!res)
break;
}
}
return flow-res;
}
ll dinic(int s,int t)
{
int i;
ll ans=0,tmp;
while (bfs(s,t))
{
for (i=1;i<=TT;i++)
cur[i]=head[i];
tmp=dfs(s,inf,t);
if (!tmp)
break;
ans+=tmp;
}
return ans;
}
int main()
{
int i,u,v,b,c;
ll maxflow=0;
n=read(),m=read(),S=read(),T=read();
SS=n+1,TT=SS+1;
tot=1;
for (i=1;i<=m;i++)
{
u=read(),v=read(),b=read(),c=read();
lnk(u,v,c-b);
d[u]+=b,d[v]-=b;
}
for (i=1;i<=n;i++)
if (d[i]!=0)
{
if (d[i]>0)
lnk(i,TT,d[i]),maxflow+=d[i];
else
lnk(SS,i,-d[i]);
}
lnk(T,S,inf);
maxflow-=dinic(SS,TT);
if (maxflow)
{
puts("please go home to sleep");
return 0;
}
maxflow=edge[tot].w;
edge[tot].w=edge[tot^1].w=0;
printf("%lld
",max(0ll,maxflow-dinic(T,S)));
return 0;
}