有/无源汇的上下界可行流
无源汇的上下界可行流
对于普通的网络流问题,设
而对于上下界可行流,设
实际上只是增加了流量的下限要求,即为流量平衡条件和流量限制条件
那么对于给定的一张图,设
- 设立一个超级源点和超级汇点,记为
s 和t - 对于原先图中的一条边
(u,v) ,在u 和v 之间连一条容量为max_{u,v} - min_{u,v} 的边 - 记录一个
d 数组,用以记录节点i 的所有入边的容量下限之和与所有出边的容量下限之和的差 - 对于一个节点
i ,如果其d[i] 小于0 ,那么就从该节点向t 连一条容量为-d[i] 的边 - 对于一个节点
i ,如果其d[i] 大于0 ,那么就从s 向该节点连一条容量为d[i] 的边 - 以
s 为起点,t 为终点跑一遍最大流,再加上所有边的容量下限即得答案
考虑这种建边方式的正确性
因为我们有一个容量上限和一个容量下限,而容量下限的条件并不容易满足,所以考虑对该点容量的上限和下限同时减去下限的容量,那么这里就可以将容量下限的限制条件消除,那么新的图上的边的当前流量为
但是显然这样是不对的,所以我们考虑对这个节点进行补流,而根据流量平衡条件,实际上一个节点在最大流中最后的流量分配可以不用关系,只需要保证其流量的平衡
那么为了
即
整理可得:
所以在上面就需要维护一个
最后得到的原图和当前图的流量都是平衡的,且满足其流量限制条件
有源汇的上下界最大流
- P5192 Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流
有源汇的上下界网络流模板
仍然建立超级源点
那么先以
因为在存在
那么考虑这道题
类似二分图,可以将天数置为左侧的点,而少女置为右侧的点,将源点向天数连边,将少女向汇点连边,之后天数与少女之间再连边,一个少女至少有
跑有源汇的上下界最大流即可
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<cstring>
#define ll long long
#define ld long double
inline ll read()
{
ll x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
const ll inf=1e9+10;
const ll maxn=1e6+10;
const ll maxm=2e6+10;
ll n,m,s,t,s1,t1,s2,t2,sum,maxflow,tot=1;
ll head[maxm<<1],d[maxn],now[maxn],g[maxn],D[maxn];
struct node
{
ll v,nxt,cap;
} e[maxm<<1];
inline void add(ll u,ll v,ll cap)
{
e[++tot].v=v;
e[tot].cap=cap;
e[tot].nxt=head[u];
head[u]=tot;
}
inline bool bfs()
{
std::queue<ll> q;
memset(d,0,sizeof(d));
q.push(s);
d[s]=1;
now[s]=head[s];
while(q.size())
{
ll u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt)
{
ll v=e[i].v;
ll w=e[i].cap;
if(!d[v]&&w)
{
d[v]=d[u]+1;
now[v]=head[v];
q.push(v);
if(v==t) return true;
}
}
}
return false;
}
inline ll dinic(ll x,ll flow)
{
if(x==t||!flow) return flow;
ll ret=flow;
for(int i=now[x];i&&ret;i=e[i].nxt)
{
ll v=e[i].v;
ll w=e[i].cap;
now[x]=i;
if(d[v]==d[x]+1&&w)
{
ll k=dinic(v,std::min(ret,w));
if(!k) d[v]=0;
e[i].cap-=k;
e[i^1].cap+=k;
ret-=k;
}
}
return flow-ret;
}
int main(void)
{
while(scanf("%lld %lld",&n,&m)!=EOF)
{
memset(head,0,sizeof(head));
memset(d,0,sizeof(d));
memset(now,0,sizeof(now));
tot=1;
sum=0,maxflow=0;
s1=0,t1=n+m+1;
s2=t1+1,t2=t1+2;
for(int i=1;i<=m;i++)
{
scanf("%lld",&g[i]);
add(i+n,t1,inf-g[i]);
add(t1,i+n,0);
d[t1]+=g[i];
d[i+n]-=g[i];
}
for(int i=1;i<=n;i++)
{
ll c,v,l,r;
scanf("%lld",&c);
scanf("%lld",&D[i]);
add(s1,i,D[i]);
add(i,s1,0);
for(int j=1;j<=c;j++)
{
scanf("%lld %lld %lld",&v,&l,&r);
v++;
add(i,v+n,r-l);
add(v+n,i,0);
d[v+n]+=l;
d[i]-=l;
}
}
for(int i=0;i<=n+m+1;i++)
{
// printf("%lld\n",d[i]);
if(d[i]<0) add(i,t2,-d[i]),add(t2,i,0);
if(d[i]>0) add(s2,i,d[i]),add(i,s2,0),sum+=d[i];
}
add(t1,s1,inf);
add(s1,t1,0);
s=s2,t=t2;
while(bfs())
{
ll flow;
while(flow=dinic(s,inf)) maxflow+=flow;
}
if(maxflow<sum)
{
printf("-1\n\n");
continue;
}
s=s1,t=t1;
maxflow=0;
maxflow+=e[tot].cap;
e[tot].cap=0;
e[tot-1].cap=0;
while(bfs())
{
ll flow;
while(flow=dinic(s,inf)) maxflow+=flow;
}
printf("%lld\n\n",maxflow);
}
return 0;
}
有源汇的上下界最小流
建图方式与无源汇的上下界可行流相同
先求
因为最初没有
最小费用可行流
- P4043 [AHOI2014/JSOI2014]支线剧情
建边方式与有源汇的上下界可行流相同,只是加入了花费,建图的时候对原图中对应的边连花费为
注意这里有上下界的费用流中是指满足流量限制条件和流量平衡条件下的最小费用,而并不用满足其最大流的限制
那么本题中,源点实际上就是
同时,对于原图中就有的边,在网络流图中的边的要求就是容量下限为
按照最小费用可行流的建图方式去建图,然后跑最小费用最大流,最后将所有边的花费加上即可(因为有花费限制的边的容量下限为
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<cstring>
#define ll long long
#define ld long double
inline ll read()
{
ll x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
const ll inf=1e9+10;
const ll maxn=15000+10;
const ll maxm=2e6;
ll n,s,t,ss,tt,tot=1,maxflow,mincost;
ll head[maxm<<1],d[maxn],flow[maxn],cost[maxn],pre[maxn],idx[maxn],vis[maxn];
struct node
{
ll v,nxt,cap,cost;
} e[maxm<<1];
inline void add(ll u,ll v,ll cap,ll cost)
{
e[++tot].v=v;
e[tot].cap=cap;
e[tot].cost=cost;
e[tot].nxt=head[u];
head[u]=tot;
}
inline bool spfa()
{
std::queue<ll> q;
memset(flow,0x3f,sizeof(flow));
memset(cost,0x3f,sizeof(cost));
memset(vis,0,sizeof(vis));
q.push(ss);
vis[ss]=1;
cost[ss]=0;
pre[tt]=-1;
while(q.size())
{
ll u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].nxt)
{
ll v=e[i].v;
ll co=e[i].cost;
ll cap=e[i].cap;
if(cost[v]>cost[u]+co&&cap)
{
cost[v]=cost[u]+co;
flow[v]=std::min(flow[u],cap);
pre[v]=u;
idx[v]=i;
if(!vis[v])
{
q.push(v);
vis[v]=1;
}
}
}
}
return pre[tt]!=-1;
}
inline void Min_Cost_Max_Flow()
{
while(spfa())
{
maxflow+=flow[tt];
mincost+=flow[tt]*cost[tt];
ll now=tt;
while(now!=ss)
{
e[idx[now]].cap-=flow[tt];
e[idx[now]^1].cap+=flow[tt];
now=pre[now];
}
}
}
int main(void)
{
n=read();
s=1,t=n+1;
ss=0,tt=n+2;
for(int i=1;i<=n;i++)
{
ll k=read();
for(int j=1;j<=k;j++)
{
ll v=read(),w=read();
d[v]++;
d[i]--;
mincost+=w;
add(i,v,inf,w);
add(v,i,0,-w);
}
}
for(int i=2;i<=n;i++)
{
add(i,t,inf,0);
add(t,i,0,0);
}
for(int i=1;i<=t;i++)
{
if(d[i]<0) add(i,tt,-d[i],0),add(tt,i,0,0);
if(d[i]>0) add(ss,i,d[i],0),add(i,ss,0,0);
}
add(t,s,inf,0),add(s,t,0,0);
Min_Cost_Max_Flow();
printf("%lld\n",mincost);
return 0;
}