给定一个包含 n n n 个点 m m m 条边的有向图,并给定每条边的容量,边的容量非负。
其中有 S c S_c Sc 个源点, T c T_c Tc 个汇点。
图中可能存在重边和自环。
求整个网络的最大流。
输入格式
第一行包含四个整数 n , m , S c , T c n,m,S_c,T_c n,m,Sc,Tc。
第二行包含 S c S_c Sc 个整数,表示所有源点的编号。
第三行包含 T c T_c Tc 个整数,表示所有汇点的编号。
接下来 m m m 行,每行三个整数 u , v , c u,v,c u,v,c,表示从点 u u u 到点 v v v 存在一条有向边,容量为 c c c。
点的编号从 1 1 1 到 n n n。
输出格式
输出一个整数表示整个网络的最大流。
数据范围
2
≤
n
≤
10000
2 \le n \le 10000
2≤n≤10000,
1
≤
m
≤
1
0
5
1 \le m \le 10^5
1≤m≤105,
0
≤
c
≤
10000
0 \le c \le 10000
0≤c≤10000,
保证源点集合和汇点集合没有交集。
输入样例:
4 5 2 2
2 4
1 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
输出样例:
70
思路
- 对于这么多的源点和汇点,我们尝试建立虚拟源点和汇点,然后从虚拟源点向所有源点连一条正无穷的边,从所有汇点往虚拟汇点连一条正无穷的边。我们也很容易证明:流量肯定守恒,容量限制。
AC 代码
//多源汇最短路问题,我们可以建一个虚拟源点,然后跟那些起点连起来,边权为无穷
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1e4+10,M = (N+1e5+10)*2,INT = 1e8;
int e[M],ne[M],f[M],h[N],idx;
int d[N],cur[N];
int n,m,S,T;
int s1,t1;
void add(int a,int b,int c){
e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
}
bool bfs(){
queue<int>q;
memset(d,-1,sizeof d);
d[S]=0;
cur[S]=h[S];
q.push(S);
while(q.size()){
int t=q.front();
q.pop();
for(int i=h[t];~i;i=ne[i]){
int ver=e[i];
if(d[ver]==-1&&f[i]){
d[ver]=d[t]+1;
cur[ver]=h[ver];
if(ver==T)return true;
q.push(ver);
}
}
}
return false;
}
int find(int u,int lim){
if(u==T)return lim;
int flow=0;
for(int i=cur[u];~i&&flow<lim;i=ne[i]){
int ver=e[i];
cur[u]=i;
if(d[ver]==d[u]+1&&f[i]){
int t=find(ver,min(f[i],lim-flow));
if(!t)d[ver]=-1;
f[i]-=t,f[i^1]+=t,flow+=t;
}
}
return flow;
}
int dinic(){
int r=0,flow;
while(bfs())while((flow=find(S,INT)))r+=flow;
return r;
}
int main(){
cin>>n>>m>>s1>>t1;
S=0,T=n+1;
memset(h,-1,sizeof h);
while(s1--){
int x;
cin>>x;
add(S,x,INT);
}
while(t1--){
int x;
cin>>x;
add(x,T,INT);
}
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
cout<<dinic();
return 0;
}