引言
网络流费用流类型的题目一般的难点都在于建图,本文主要总结作者在做习题过程中也遇到的各种困难,以例题的形式进行讲解。
关于网络流费用流的相关原理网上可以找到许多写得非常详细的博客,本文就不再次累述相关知识点,本文将给出更多的例题加以分析,熟悉如何把题目转化为图,如何巧妙建图。下面给出几个写得不错的博客加以参考。
网络流费用流入门博客参考:
https://blog.csdn.net/yjr3426619/article/details/82808303
https://blog.csdn.net/x_y_q_/article/details/51999466
https://blog.csdn.net/Tramp_1/article/details/52618334
https://blog.csdn.net/A_Comme_Amour/article/details/79356220
例题分析
例题一
题目来源:International Collegiate Programming Contest (ACM-ICPC) World Finals 2015C题:Catering
题面:
一句话题意:给定一张
n
+
1
(
1
≤
n
≤
100
)
\mathbf{n+1(1\le n\le 100)}
n+1(1≤n≤100)个点(点的编号从0开始)的图,对于任意两个点
i
,
j
(
i
<
j
)
\mathbf{i,j(i<j)}
i,j(i<j)都存在一条i到j的有向边(带边权
0
≤
边
权
≤
1000000
\mathbf{0\le 边权\le1000000}
0≤边权≤1000000)。有k个人从0点出发,每个人都能沿着任意边走到某个点(可以是0点,也就是不动)停下来,要求对于任意点
i
(
1
≤
i
≤
n
)
\mathbf{i(1\le i\le n)}
i(1≤i≤n)恰好存在一个人经过,问所有人经过的边权和最大为多少。
题解:本题的难点在于建图,事实上如果掌握了模型转化的方法不需要理解网络流原理也能做出本题。分析本题的图的特点,假设每个人走动形成的轨迹是一张新的图,在这张图中,0点的出度小于等于k(不为0),其他点分为两种,对于非终点而言的点的入度和出度都等于1,终点的入度为1,出度为0。因此只要能够构造出满足这种出入度关系的图并且让图的权值和最大即可。
考虑建立如下网络流模型:
对于每个点
i
(
i
≤
n
)
\mathbf{i(i\le n)}
i(i≤n)都建立一个镜像节点
i
′
=
i
+
n
\mathbf{i'=i+n}
i′=i+n,这样连边关系由原来的
i
−
>
j
\mathbf{i->j}
i−>j改为
i
−
>
j
′
\mathbf{i->j'}
i−>j′,不过连边还得改成网络流的连边方式,容量设为1,费用设为原来的边权即可。然后设源点s,汇点t,从s点向节点0连一条容量为k,费用为0的边,向节点1~n都连一条容量为1,费用为0的边。对于所有n+1 ~ n+n的节点都向t点连一条容量为1,费用为0的边。连边示意图如下图一:
那么为什么这样连边就能够保证合法呢。首先是0点的出度一定是小于等于k的,因为从s点出发的流量最大只有k,此外点n+1的唯一流量只能来源于0点,这条边
(
指
0
−
>
n
+
1
这
条
边
)
\mathbf{(指0 -> n+1这条边)}
(指0−>n+1这条边)一定会被算到最大流中,故0点出度至少是1,所有合法。再考虑除0点外在原图中每个点的入度,由于从s点出发的流量是不小于流入t点的最大流量,故n+1~n+n的节点都会向t点贡献流量,由于每个节点流量为1的限制,并且n+1 ~ n+n的节点的流量都必定来自0 ~ n-1 ,相当于原图中1~n的节点入度都为1。而原图中每个节点(1 ~ n)的出度可为0可为1,对应着网络流中的1~n号节点向n+1 ~ n+n号节点的连边关系(如果不连边为0否则为1,由于1~n号节点流量最大为1,故出度最大为1)
总上所述,由网络流形成的图具有合法的连边关系,由于最大流是n,显然跑出来的费用流也一定是所有情况中最大的,建图搞定后就只需要从s到t点跑一下最大流即可。
代码:
#include <bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
#define pi pair<int,int>
#define mk(a,b) make_pair(a,b)
#define mygc(c) (c)=getchar()
#define mypc(c) putchar(c)
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 400;
const int maxm = 200005;
const int inf = 2147483647;
typedef long long ll;
const double eps = 1e-9;
const long long INF = 9223372036854775807ll;
ll qpow(ll a,ll b,ll c){ll ans=1;while(b){if(b&1)ans=ans*a%c;a=a*a%c;b>>=1;}return ans;}
inline void rd(int *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(ll *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);}
inline void rd(db *x){scanf("%lf",x);}
inline int rd(char c[]){int i,s=0;for(;;){mygc(i);if(i!=' '&&i!='\n'&&i!='\r'&&i!='\t'&&i!=EOF) break;}c[s++]=i;for(;;){mygc(i);if(i==' '||i=='\n'||i=='\r'||i=='\t'||i==EOF) break;c[s++]=i;}c[s]='\0';return s;}
inline void rd(int a[],int n){FOR(i,0,n)rd(&a[i]);}
inline void rd(ll a[],int n){FOR(i,0,n)rd(&a[i]);}
template <class T, class S> inline void rd(T *x, S *y){rd(x);rd(y);}
template <class T, class S, class U> inline void rd(T *x, S *y, U *z){rd(x);rd(y);rd(z);}
template <class T, class S, class U, class V> inline void rd(T *x, S *y, U *z, V *w){rd(x);rd(y);rd(z);rd(w);}
inline void wr(int x){if(x < 10) putchar('0' + x); else wr(x / 10), wr(x % 10);}
inline void wr(int x, char c){int s=0,m=0;char f[10];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(ll x, char c){int s=0,m=0;char f[20];if(x<0)m=1,x=-x;while(x)f[s++]=x%10,x/=10;if(!s)f[s++]=0;if(m)mypc('-');while(s--)mypc(f[s]+'0');mypc(c);}
inline void wr(db x, char c){printf("%.15f",x);mypc(c);}
inline void wr(const char c[]){int i;for(i=0;c[i]!='\0';i++)mypc(c[i]);}
inline void wr(const char x[], char c){int i;for(i=0;x[i]!='\0';i++)mypc(x[i]);mypc(c);}
template<class T> inline void wrn(T x){wr(x,'\n');}
template<class T, class S> inline void wrn(T x, S y){wr(x,' ');wr(y,'\n');}
template<class T, class S, class U> inline void wrn(T x, S y, U z){wr(x,' ');wr(y,' ');wr(z,'\n');}
template<class T, class S, class U,class H> inline void wrn(T x, S y, U z,H h){wr(x,' ');wr(y,' ');wr(z,' ');wr(h,'\n');}
template<class T> inline void wra(T x[], int n){int i;if(!n){mypc('\n');return;}FOR(i,0,n-1)wr(x[i],' ');wr(x[n-1],'\n');}
struct MCMF{
int h[maxn],ee,n,m,maxflow=0,mincost=0,flow[maxn],cost[maxn],father[maxn],inq[maxn];
struct Edge{
int v,w,c,next;
}edge[maxm];
queue<int> q;
void init(int nn){
n=nn;
memset(h,-1,sizeof(int)*(nn+1));
}
void addedge(int u,int v,int w,int c){
edge[ee]=Edge{v,w,c,h[u]};
h[u]=ee++;
edge[ee]=Edge{u,0,-c,h[v]};
h[v]=ee++;
}
bool bfs(int s,int t){
memset(flow,0,sizeof(int)*(n+1));
memset(cost,0x3f,sizeof(int)*(n+1));
memset(inq,0,sizeof(int)*(n+1));
while(!q.empty())q.pop();
cost[s]=0;
q.push(s);
inq[s]=1;
flow[s]=inf;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=h[u];i!=-1;i=edge[i].next){
if(edge[i].w && cost[edge[i].v]>cost[u]+edge[i].c){
cost[edge[i].v]=cost[u]+edge[i].c;
flow[edge[i].v]=min(flow[u],edge[i].w);
father[edge[i].v]=i;
if(!inq[edge[i].v]){
inq[edge[i].v]=1;
q.push(edge[i].v);
}
}
}
inq[u]=0;
}
return flow[t]>0;
}
int sfpa(int s,int t){
while(bfs(s,t)){
maxflow+=flow[t];
mincost+=cost[t]*flow[t];
int i=father[t];
edge[i].w-=flow[t];
edge[i^1].w+=flow[t];
while(edge[i^1].v!=s){
i=father[edge[i^1].v];
edge[i].w-=flow[t];
edge[i^1].w+=flow[t];
}
}
return mincost;
}
}mcmf;
int main(){
int n,k,s,t;
rd(&n,&k);
mcmf.init(2*n+2);
s=2*n+1,t=2*n+2;
FOR(i,0,n){
if(!i)mcmf.addedge(s,i,k,0);else mcmf.addedge(s,i,1,0);
mcmf.addedge(i+n+1,t,1,0);
FOR(j,i+1,n+1){
int x;
rd(&x);
mcmf.addedge(i,j+n,1,x);
}
}
wrn(mcmf.sfpa(s,t));
}
例题二
待补充。。。