EK算法的思路:
1.通过BFS扩展合法节点,找到汇点,并记录pre前一个节点,如果找不到增广路,算法结束
2.通过BFS的记录,从汇点回溯到起点,记录下每条边的 res残流
3.将所有经过的边减去res 反向边加上
4.重复上面的步骤,直到找不到增广路,结束
1.朴素EK:
通过邻接矩阵来储存
但是复杂度太大 N^2
附上源代码 :
const int MAX=220;
int pre[MAX];
int flow[MAX][MAX];
int cap[MAX][MAX];
int res[MAX];//res残量
int Edmonds_Karp(int s,int e){ //EK算法
int maxflow=0;
mms(flow,0);
mms(pre,0);
queue<int>q;
while (true) {
mms(res,0);
res[s]=INF;
q.push(s);
while (!q.empty()) {
int u=q.front();
q.pop();
for(int v=1;v<=e;v++){
if(!res[v]&&cap[u][v]>flow[u][v]){
res[v]=min(res[u],cap[u][v]-flow[u][v]);
pre[v]=u;
q.push(v);
}
}
}
while(res[e]==0) return maxflow;
for(int u=e;u!=s;u=pre[u]){
flow[pre[u]][u]+=res[e];
flow[u][pre[u]]-=res[e];
}
maxflow+=res[e];
}
}
int main(){
int t;
cin>>t;
while (t--) {
//
mms(cap,0);
int n,m;
cin>>n>>m;
//for
for(int i=0;i<m;i++){
int u,v,w;
cin>>u>>v>>w;
cap[u][v]+=w;
}
// cout<<Edmonds_Karp(1,n)<<endl;
int k=1;
cout<<"Case "<<k++<<": "<<Edmonds_Karp(1,n)<<endl;
}
// int u,v,w;
// cap[u][v]+=w;//+=考虑重边的问题
//
}
2.无优化的Dinic算法:
const int MAX=10010;
int n,m,s,t;
struct E{
int next,to,w; //对应边的残量
}e[MAX];
int head[MAX];
int cnt=0; //反边直接^
void add(int u,int v,int w){ //前向星存图
e[cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
}
int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
while (!q.empty()) { //先清空
q.pop();
}
mms(dep,0);
dep[s]=1;
q.push(s);
while (!q.empty()) {
int u=q.front();
q.pop();
for(re int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(e[i].w>0&&dep[v]==0){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
return 1;
}
int dfs(int u,int dist){ //u是当前的节点 dist是当前的流量
if(u==t)return dist;
for(re int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(dep[v]==dep[u]+1&&e[i].w!=0){
int d=dfs(v,min(dist,e[i].w));
if(d>0){ //增广成功
e[i].w-=d; //正边-
e[i^1].w+=d;//反向边+
return d; //向上传递
}
}
}
return 0;
}
int Dinic(){
int ans=0;
while (bfs()) {
ans+=dfs(s,INF);
}
return ans;
}
int main(){
mms(head,-1);
n=read();
m=read();
s=read();
t=read();
for(re int i=1,x,y,z;i<=m;i++){
x=read();
y=read();
z=read();
add(x,y,z);
add(y,x,0);
}
cout<<Dinic()<<endl;
return 0;
}
3.Dinic+当前弧优化:
const int MAX=10010;
int n,m,s,t;
struct E{
int next,to,w; //对应边的残量
}e[MAX];
int head[MAX];
int cnt=0; //反边直接^
void add(int u,int v,int w){ //前向星存图
e[cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
}
//当前弧优化部分
int rec[MAX]; //记录u循环到了那条边
//end
int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
while (!q.empty()) { //先清空
q.pop();
}
mms(dep,0);
dep[s]=1;
q.push(s);
while (!q.empty()) {
int u=q.front();
q.pop();
for(re int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(e[i].w>0&&dep[v]==0){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
return 1;
}
int dfs(int u,int dist){ //u是当前的节点 dist是当前的流量
if(u==t)return dist;
//优化部分当前弧
for(re int &i=rec[u];i!=-1;i=e[i].next){ //&可以起到增加i的同时增加cur[u]的值
int v=e[i].to;
if(dep[v]==dep[u]+1&&e[i].w!=0){
int d=dfs(v,min(dist,e[i].w));
if(d>0){ //增广成功
e[i].w-=d; //正边-
e[i^1].w+=d;//反向边+
return d; //向上传递
}
}
}
return 0;
//未优化
// for(re int i=head[u];i!=-1;i=e[i].next){
// int v=e[i].to;
// if(dep[v]==dep[u]+1&&e[i].w!=0){
// int d=dfs(v,min(dist,e[i].w));
// if(d>0){ //增广成功
// e[i].w-=d; //正边-
// e[i^1].w+=d;//反向边+
// return d; //向上传递
// }
// }
// }
// return 0;
}
int Dinic(){
int ans=0;
//未优化
// while (bfs()) {
// ans+=dfs(s,INF);
// }
//当前弧优化
while (bfs()) {
for(re int i=1;i<=n;i++){
rec[i]=head[i]; //每次建完图以后都要把rec重置到每个点的第一条边
}
ans+=dfs(s,INF);
}
return ans;
}
int main(){
mms(head,-1);
n=read();
m=read();
s=read();
t=read();
for(re int i=1,x,y,z;i<=m;i++){
x=read();
y=read();
z=read();
add(x,y,z);
add(y,x,0);
}
cout<<Dinic()<<endl;
return 0;
}
4.Dinic 多路增广优化+当前弧优化:
const int MAX=10010;
int n,m,s,t;
struct E{
int next,to,w; //对应边的残量
}e[MAX];
int head[MAX];
int cnt=0; //反边直接^
void add(int u,int v,int w){ //前向星存图
e[cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
}
//当前弧优化部分
int rec[MAX]; //记录u循环到了那条边
//end
int maxflow;
int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
while (!q.empty()) { //先清空
q.pop();
}
mms(dep,0);
dep[s]=1;
q.push(s);
while (!q.empty()) {
int u=q.front();
q.pop();
for(re int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(e[i].w>0&&dep[v]==0){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
return 1;
}
//当前弧优化
//int dfs(int u,int dist){ //u是当前的节点 dist是当前的流量
// if(u==t)return dist;
// //优化部分当前弧
// for(re int &i=rec[u];i!=-1;i=e[i].next){ //&可以起到增加i的同时增加cur[u]的值
// int v=e[i].to;
// if(dep[v]==dep[u]+1&&e[i].w!=0){
// int d=dfs(v,min(dist,e[i].w));
// if(d>0){ //增广成功
// e[i].w-=d; //正边-
// e[i^1].w+=d;//反向边+
// return d; //向上传递
// }
// }
// }
// return 0;
//
//
//
// //未优化
for(re int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(dep[v]==dep[u]+1&&e[i].w!=0){
int d=dfs(v,min(dist,e[i].w));
if(d>0){ //增广成功
e[i].w-=d; //正边-
e[i^1].w+=d;//反向边+
return d; //向上传递
}
}
}
return 0;
//}
//多路增广优化
int dfs(int u,int flow){
if(u==t){
maxflow+=flow;
return flow;
}
int used=0; //表示这个点的流量用了多少
for(re int &i=rec[u];~i;i=e[i].next){
int v=e[i].to;
if(dep[v]==dep[u]+1&&e[i].w!=0){
int d=dfs(v,min(flow-used,e[i].w));
if(d>0){
e[i].w-=d;
e[i^1].w+=d;
used+=d;
if(used==flow) break; //这点的流量满了 不需要找了
//dfs找到一条增广路 不需要返回 只需要修改used就行
//如果used没有到达flow的上限 就可以继续寻找
}
}
}
return used;
}
int Dinic(){
int ans=0;
//未优化
// while (bfs()) {
// ans+=dfs(s,INF);
// }
//当前弧优化
while (bfs()) {
for(re int i=1;i<=n;i++){
rec[i]=head[i]; //每次建完图以后都要把rec重置到每个点的第一条边
}
ans+=dfs(s,INF);
}
return ans;
}
int main(){
mms(head,-1);
n=read();
m=read();
s=read();
t=read();
for(re int i=1,x,y,z;i<=m;i++){
x=read();
y=read();
z=read();
add(x,y,z);
add(y,x,0);
}
cout<<Dinic()<<endl;
return 0;
}
5.ISAP+gap优化+多路增广:
const int MAX=10010;
int n,m,s,t;
struct E{
int next,to,w; //对应边的残量
}e[MAX<<1]; //双向边*2
int head[MAX];
int gap[MAX]; //gap是深度为i的点的数量 优化
int cnt=0; //反边直接^
void add(int u,int v,int w){ //前向星存图
e[cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
}
//当前弧优化部分
int rec[MAX]; //记录u循环到了那条边
//end
int maxflow;
int dep[MAX]; //深度
queue<int >q;
int bfs(){ //bfs分层图
mms(dep,-1);
mms(gap,0);
q.push(t);
dep[t]=0;
gap[0]=1;
while (!q.empty()) {
int u=q.front();
q.pop();
for(re int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(dep[v]!=-1) continue; //防止重复修改某个点
q.push(v);
dep[v]=dep[u]+1;
gap[dep[v]]++;
}
}
if(dep[t]==0) return 0; //当汇点的深度不存在时候 说明不存在分层图 同时也说明了不存在增广路
return 1;
}
int dfs(int u,int flow){
if(u==t) {
maxflow+=flow;
return flow;
}
int used=0;
for(re int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(e[i].w&&dep[v]+1==dep[u]){
int d=dfs(v,min(flow-used,e[i].w));
if(d>0){
e[i].w-=d;
e[i^1].w+=d;
used+=d;
}
if(used==flow){
return used;
}
}
}
--gap[dep[u]];
if(!gap[dep[u]]) dep[s]=n+1;
dep[u]++;
gap[dep[u]]++;
return used;
}
void ISAP(int s,int t){
bfs();//从t到s跑一遍bfs 标记初始深度
while (dep[s]<n) {
dfs(s,INF);
}
//终止条件 dep[s]<n
//每走一次增广路 s的层数+1 若一直没有断层 最多跑n-dep(刚bfs完s的深度)
}
int main(){
mms(head,-1);
n=read();
m=read();
s=read();
t=read();
for(re int i=1,x,y,z;i<=m;i++){
x=read();
y=read();
z=read();
add(x,y,z);
add(y,x,0);
}
ISAP(s,t);
cout<<maxflow<<endl;
}