各种网络流模板整合(EK,Dinic,推送-重贴标签,前置重贴标签)

本文不涉及讲解,仅罗列各种算法模板(都是自己写的,不一定最优,且一个算法不一定只有一种)——不定期更新


目录

  • Edmond-Karp(O(VE^2))——包含邻接矩阵,邻接表,储存边三种
  • Dinic(O(V^2E))——包含基础与当前弧优化两种
  • 推送重贴标签(O(V^2E))——一种
  • 前置重贴标签(O(V^3))——一种
  • 注:最后两种来自与《算法导论》,不一定保证最优实现,且该两种算法下界大致为\Omega (V^2),故要谨慎使用!!(以及残存容量实时更新,不单独计算并允许双向边存在)

Edmond-Karp算法

1.邻接链表(第一次写网络流的最好尝试)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

int N, M, st, se;
int cap[205][205], pre[205];
bool vis[205];
int Edmonds_Karp(int s, int t);

int main(){
  ios::sync_with_stdio(false);
  int i;
  cin >> N >> M >> st >> se;
  for(i = 1; i <= M; i++){
    int x, y, w;
    cin >> x >> y >> w;
    cap[x][y] = w;
  }
  cout << Edmonds_Karp(st, se) << endl;
  return 0;
}

bool bfs(int s, int t);//寻找残存网络中最短增广路
int Edmonds_Karp(int s, int t){
  int i, cf_min, max_flow;
  max_flow = 0;
  while(bfs(s, t)){
    cf_min = 1e9 + 7;
    for(i = t; i != s; i = pre[i])
      cf_min = min(cf_min, cap[pre[i]][i]);
    for(i = t; i != s; i = pre[i]){
      cap[pre[i]][i] -= cf_min;
      cap[i][pre[i]] += cf_min;
    }
    max_flow += cf_min;
  }
  return max_flow;
}

bool bfs(int s, int t){
  int i;
  memset(vis, 0, sizeof(vis));
  queue<int> Q;
  Q.push(s), vis[s] = true;
  while(!Q.empty()){
    auto u = Q.front(); Q.pop();
    for(i = 1; i <= N; i++){
      if(!vis[i] && cap[u][i] > 0){
        Q.push(i), vis[i] = true;
        pre[i] = u;
        if(i == t) return true;
      }
    }
  }
  return false;
}

2.邻接表

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
struct edge {
  int next, now, to, c;
} e[MAXM * 2];
int head[MAXN], cnt_adj;
void add_edge(int x, int y, int z);
int N, M, st, se;
int Edmonds_Karp(int s, int t);

int main(){
  ios::sync_with_stdio(false);
  int i;
  cin >> N >> M >> st >> se;
  cnt_adj = 1;
  for(i = 1; i <= M; i++){
    int x, y, z;
    cin >> x >> y >> z;
    add_edge(x, y, z);
    add_edge(y, x, 0);
  }
  cout << Edmonds_Karp(st, se) << endl;
  return 0;
}

int pre[MAXN];
bool EK_bfs(int s, int t);
int Edmonds_Karp(int s, int t){
  int maxflow = 0;
  while(EK_bfs(s, t)){
    int i, cf_min = 1e9 + 7;
    for(i = pre[t]; i; i = pre[e[i].now]){
      cf_min = min(cf_min, e[i].c);
    }
    for(i = pre[t]; i; i = pre[e[i].now]){
      e[i].c -= cf_min;
      e[i ^ 1].c += cf_min;
    }
    maxflow += cf_min;
  }
  return maxflow;
}

bool vis[MAXN];
bool EK_bfs(int s, int t){
  memset(vis, 0, sizeof(vis));
  int i;
  queue<int> Q;
  Q.push(s), vis[s] = true;
  while(!Q.empty()){
    int u = Q.front(); Q.pop();
    for(i = head[u]; i; i = e[i].next){
      int v = e[i].to, cf = e[i].c;
      if(!vis[v] && cf){
        Q.push(v), vis[v] = true;
        pre[v] = i;
        if(v == t) return true;
      }
    }
  }
  return false;
}

void add_edge(int x, int y, int z){
  e[++cnt_adj].to = y;
  e[cnt_adj].now = x;
  e[cnt_adj].c = z;
  e[cnt_adj].next = head[x];
  head[x] = cnt_adj;
}

3.储存边

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
//储存边与对于顶点的反向hash
struct node {
  int u, v, c;
} e[MAXM * 2];
vector<int> hash_e[MAXN];
int N, M, st, se;
int pre[MAXN];
bool vis[MAXN];
int Edmonds_Karp(int s, int t);

int main(){
  ios::sync_with_stdio(false);
  int i, j;
  cin >> N >> M >> st >> se;
  for(j = 0; j < M; j++){
    i = 2 * j;
    int x, y, z;
    cin >> x >> y >> z;
    e[i].u = x, e[i].v = y, e[i].c = z;
    e[i ^ 1].u = y, e[i ^ 1].v = x;
    hash_e[x].push_back(i);
    hash_e[y].push_back(i ^ 1);
  }
  cout << Edmonds_Karp(st, se) << endl;
  return 0;
}

bool EK_bfs(int s, int t);
int Edmonds_Karp(int s, int t){
  int i, cap_min, maxflow;
  maxflow = 0;
  while(EK_bfs(s, t)){
    cap_min = 1e9 + 7;
    for(i = t; i != s; i = e[pre[i]].u)
      cap_min = min(cap_min, e[pre[i]].c);
    for(i = t; i != s; i = e[pre[i]].u){
      e[pre[i]].c -= cap_min;
      e[pre[i] ^ 1].c += cap_min;
    }
    maxflow += cap_min;
  }
  return maxflow;
}

bool EK_bfs(int s, int t){
  memset(vis, 0, sizeof(vis));
  int i, j;
  queue<int> Q;
  Q.push(s), vis[s] = true;
  while(!Q.empty()){
    auto u = Q.front(); Q.pop();
    for(i = 0; i < (int)hash_e[u].size(); i++){
      j = hash_e[u][i];
      int v = e[j].v;
      if(!vis[v] && e[j].c > 0){
        Q.push(v), vis[v] = true;
        pre[v] = j;
        if(v == t) return true;
      }
    }
  }
  return false;
}

Dinic算法

1.基本写法

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
struct edge {
  int next, to, c;
} e[MAXM * 2];
int head[MAXN], cnt_adj;
void add_edge(int, int, int);
int N, M, st, se;
int Dinic(int s, int t);

int main(){
  ios::sync_with_stdio(false);
  int i;
  cin >> N >> M >> st >> se;
  cnt_adj = 1;//2i-1与2i对应一组边,从2开始(不可从0)
  for(i = 1; i <= M; i++){
    int x, y, z;
    cin >> x >> y >> z;
    add_edge(x, y, z);
    add_edge(y, x, 0);
  }
  cout << Dinic(st, se) << endl;
  return 0;
}

int dep[MAXN];
bool Dinic_bfs(int s, int t);
int Dinic_dfs(int u, int cf_min, int t);
int Dinic(int s, int t){
  int max_flow = 0;
  while(Dinic_bfs(s, t))
    while(int val = Dinic_dfs(s, 1e9 + 7, t))
      max_flow += val;
  return max_flow;
}

//分层
bool Dinic_bfs(int s, int t){
  int i;
  memset(dep, 0, sizeof(dep));
  queue<int> Q;
  Q.push(s), dep[s] = 1;
  while(!Q.empty()){
    auto u = Q.front(); Q.pop();
    //cout << u << endl;
    for(i = head[u]; i; i = e[i].next){
      int v = e[i].to, cf = e[i].c;
      //cout << v << " " << cf << endl;
      if(cf > 0 && !dep[v]){
        dep[v] = dep[u] + 1;
        Q.push(v);
      }
    }
    //cout << endl;
  }
  //cout << endl;
  if(!dep[t]) return false;
  return true;
}

int Dinic_dfs(int u, int cf_min, int t){
  if(u == t) return cf_min;
  int i;
  for(i = head[u]; i; i = e[i].next){
    int v = e[i].to, cf = e[i].c;
    if(dep[v] == dep[u] + 1 && cf > 0){
      int val = Dinic_dfs(v, min(cf_min, cf), t);
      if(val > 0){//增广成功
        e[i].c -= val;
        e[i ^ 1].c += val;
        return val;
      }
    }
  }
  return 0;
}

void add_edge(int x, int y, int z){
  e[++cnt_adj].to = y;
  e[cnt_adj].c = z;
  e[cnt_adj].next = head[x];
  head[x] = cnt_adj;
}

2.当前弧优化

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
struct edge {
  int next, to, c;
} e[MAXM * 2];
int head[MAXN], cnt_adj;
void add_edge(int x, int y, int z);
int N, M, st, se;
int Dinic(int s, int t);

int main(){
  ios::sync_with_stdio(false);
  int i;
  cin >> N >> M >> st >> se;
  cnt_adj = 1;
  for(i = 1; i <= M; i++){
    int x, y, z;
    cin >> x >> y >> z;
    add_edge(x, y, z);
    add_edge(y, x, 0);
  }
  cout << Dinic(st, se) << endl;
  return 0;
}

int dep[MAXN], cur[MAXN];
bool Dinic_bfs(int s, int t);
int Dinic_dfs(int u, int cf_min, int t);
int Dinic(int s, int t){
  int maxflow = 0;
  while(Dinic_bfs(s, t)){
    int i, val;
    for(i = 1; i <= N; i++)
      cur[i] = head[i];
    while((val = Dinic_dfs(s, 1e9 + 7, t)))
      maxflow += val;
  }
  return maxflow;
}

bool Dinic_bfs(int s, int t){
  int i;
  memset(dep, 0, sizeof(dep));
  queue<int> Q;
  Q.push(s), dep[s] = 1;
  while(!Q.empty()){
    auto u = Q.front(); Q.pop();
    for(i = head[u]; i; i = e[i].next){
      int v = e[i].to, cf = e[i].c;
      if(cf > 0 && !dep[v]){
        dep[v] = dep[u] + 1;
        Q.push(v);
      }
    }
  }
  if(!dep[t]) return false;
  return true;
}

int Dinic_dfs(int u, int cf_min, int t){
  if(u == t || !cf_min) return cf_min;
  for(int &i = cur[u]; i; i = e[i].next){//取地址进行操作,实时更新
    int v = e[i].to, cf = e[i].c;
    if(cf > 0 && dep[v] == dep[u] + 1){
      int val = Dinic_dfs(v, min(cf_min, cf), t);
      if(val > 0){
        e[i].c -= val;
        e[i ^ 1].c += val;
        return val;
      }
    }
  }
  return 0;
}

void add_edge(int x, int y, int z){
  e[++cnt_adj].to = y;
  e[cnt_adj].c = z;
  e[cnt_adj].next = head[x];
  head[x] = cnt_adj;
}

推送-重贴标签

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

struct edge {
  int next, to, c;
} e[200005];
int head[10005], cnt_adj;
void add_edge(int x, int y, int z);
int N, M, st, se;
int Push_and_Relabel(int s, int t);

int main(){
  ios::sync_with_stdio(false);
  int i;
  cin >> N >> M >> st >> se;
  cnt_adj = 1;
  for(i = 1; i <= M; i++){
    int x, y, z;
    cin >> x >> y >> z;
    add_edge(x, y, z);
    add_edge(y, x, 0);
  }
  cout << Push_and_Relabel(st, se) << endl;
  return 0;
}

struct node {
  int h, e;
} p[10005];
void Init_Preflow(int s);
int check(int s, int t);
bool Push(int u);
void Relabel(int u);
int Push_and_Relabel(int s, int t){
  int id;
  Init_Preflow(s);
  while((id = check(s, t))){
    if(!Push(id))
      Relabel(id);
  }
  return p[t].e;
}

int check(int s, int t){
  int i;
  for(i = 1; i <= N; i++)
    if(i != s && i != t && p[i].e)
      return i;
  return 0;
}

void Init_Preflow(int s){
  int i;
  p[s].h = N;
  for(i = head[s]; i; i = e[i].next){
    int val = e[i].c;
    p[s].e -= val;
    p[e[i].to].e += val;
    e[i].c -= val;
    e[i ^ 1].c += val;
  }
}

bool Push(int u){
  bool flag = false;
  int i;
  for(i = head[u]; i; i = e[i].next){
    int v = e[i].to, cf = e[i].c;
    if(cf && p[u].h == p[v].h + 1){
      int val = min(p[u].e, cf);
      p[u].e -= val;
      p[v].e += val;
      e[i].c -= val;
      e[i ^ 1].c += val;
      flag = true;
    }
  }
  return flag;
}

void Relabel(int u){
  int i, minn = 1e9 + 7;
  for(i = head[u]; i; i = e[i].next){
    int v = e[i].to, cf = e[i].c;
    if(cf)
      minn = min(minn, p[v].h);
  }
  if(minn == 1e9 + 7)
    minn = -1;
  p[u].h = minn + 1;
}

void add_edge(int x, int y, int z){
  e[++cnt_adj].to = y;
  e[cnt_adj].c = z;
  e[cnt_adj].next = head[x];
  head[x] = cnt_adj;
}

前置重贴标签

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

const int MAXN = 1e4 + 5;
const int MAXM = 1e5 + 5;
struct edge {
  int next, to, c, f, cf;
} e[MAXM * 2];
int head[MAXN], cnt_adj;
void add_edge(int x, int y, int z);
int N, M, st, se;
int height[MAXN], overflow[MAXN];
void Relabel_to_Front(int s, int t);

int main(){
  ios::sync_with_stdio(false);
  int i;
  cin >> N >> M >> st >> se;
  cnt_adj = 1;
  for(i = 1; i <= M; i++){
    int x, y, z;
    cin >> x >> y >> z;
    add_edge(x, y, z);
    add_edge(y, x, 0);
  }
  Relabel_to_Front(st, se);
  //for(i = 1; i <= N; i++)
  //  cout << overflow[i] << endl;
  cout << overflow[se] << endl;
  return 0;
}

struct linklist {
  int val;
  linklist *next, *pre;
};
void Discharge(int u);
void Init_Preflow(int s);
void Relabel_to_Front(int s, int t){
  Init_Preflow(s);
  int i;
  linklist *head = new linklist;
  linklist *NIL = new linklist;
  head->next = head->pre = NIL;
  head->val = NIL->val = -1;
  for(i = 1; i <= N; i++)
    if(i != s && i != t){
      linklist *x = new linklist;
      x->val = i;
      x->next = head->next;
      x->pre = head;
      x->next->pre = x;
      head->next = x;
    }

  linklist *p = head->next;
  while(p->val != -1){
    int u = p->val;
    int old_height = height[u];
    Discharge(u);
    if(old_height < height[u]){
      p->pre->next = p->next;
      p->next->pre = p->pre;

      p->next = head->next;
      p->pre = head;
      p->next->pre = p;
      head->next = p;
    }
    p = p->next;
  }
}

void Init_Preflow(int s){
  int i;
  //memset(height, 0, sizeof(height));
  //memset(overflow, 0, sizeof(overflow));
  //init e.f=0;
  height[s] = N;
  for(i = head[s]; i; i = e[i].next){
    int val = e[i].c;
    e[i].f = val;
    overflow[s] -= val;
    overflow[e[i].to] += val;
    e[i].c -= val;
    e[i ^ 1].c += val;
  }
  //for(i = 1; i <= N; i++)
  //  cout << overflow[i] << endl;
}

void Push(int u, int id);
void Relabel(int u);
void Discharge(int u){
  int i;
  i = head[u];
  if(!i) return;
  while(overflow[u]){
    if(!i){
      Relabel(u);
      i = head[u];
    }else{
      int v = e[i].to, cf = e[i].c;
      if(cf && height[u] == height[v] + 1)
        Push(u, i);
      i = e[i].next;
    }
  }
}


void Push(int u, int id){
  int val = min(overflow[u], e[id].c);
  e[id].c -= val;
  e[id ^ 1].c += val;
  overflow[u] -= val;
  overflow[e[id].to] += val;
}

void Relabel(int u){
  int i, minn = 1e9 + 7;
  for(i = head[u]; i; i = e[i].next){
    int v = e[i].to, cf = e[i].c;
    if(cf)
      minn = min(minn, height[v]);
  }
  if(minn == 1e9 + 7) minn = -1;
  height[u] = minn + 1;
}

void add_edge(int x, int y, int z){
  e[++cnt_adj].to = y;
  e[cnt_adj].c = z;
  e[cnt_adj].next = head[x];
  head[x] = cnt_adj;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值