zoukankan      html  css  js  c++  java
  • 最大流算法 ISAP 模板 和 Dinic模板

    ISAP

    // UVa11248 Frequency Hopping:使用ISAP算法,加优化
    // Rujia Liu
    
    struct Edge {
      int from, to, cap, flow;
    };
    
    struct ISAP {
      int n, m, s, t;
      vector<Edge> edges;
      vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
      bool vis[maxn];        // BFS使用
      int d[maxn];           // 从起点到i的距离
      int cur[maxn];        // 当前弧指针
      int p[maxn];          // 可增广路上的上一条弧
      int num[maxn];        // 距离标号计数
    
      void AddEdge(int from, int to, int cap) {
        edges.push_back((Edge){from, to, cap, 0});
        edges.push_back((Edge){to, from, 0, 0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
      }
    
      bool BFS() {
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(t);
        vis[t] = 1;
        d[t] = 0;
        while(!Q.empty()) {
          int x = Q.front(); Q.pop();
          for(int i = 0; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]^1];///因为t本来就是最后一个结点,一般最后都是反向边的?
            if(!vis[e.from] && e.cap > e.flow) {
              vis[e.from] = 1;
              d[e.from] = d[x] + 1;
              Q.push(e.from);
            }
          }
        }
        return vis[s];
      }
    
      void ClearAll(int n) {  ///为了重新建边
        this->n = n;
        for(int i = 0; i < n; i++) G[i].clear();
        edges.clear();
      }
    
      void ClearFlow() {  ///清除流量
        for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
      }
    
      int Augment() {  ///沿着增广边扩展flow
        int x = t, a = INF;
        while(x != s) {
          Edge& e = edges[p[x]];
          a = min(a, e.cap-e.flow);
          x = edges[p[x]].from;
        }
        x = t;
        while(x != s) {
          edges[p[x]].flow += a;
          edges[p[x]^1].flow -= a;
          x = edges[p[x]].from;
        }
        return a;
      }
    
      int Maxflow(int s, int t) {  ///求最大流
        this->s = s; this->t = t;
        int flow = 0;
        BFS();
        memset(num, 0, sizeof(num));
        for(int i = 0; i < n; i++) num[d[i]]++;
        int x = s;
        memset(cur, 0, sizeof(cur));
        while(d[s] < n) {
          if(x == t) {
            flow += Augment();
            x = s;
          }
          int ok = 0;
          for(int i = cur[x]; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if(e.cap > e.flow && d[x] == d[e.to] + 1) { // Advance
              ok = 1;
              p[e.to] = G[x][i];
              cur[x] = i; // 注意
              x = e.to;
              break;
            }
          }
          if(!ok) { /// Retreat 重新对待。因为该节点在目前的d下已经不存在增广路了,所以我们要对他进行增广
            int m = n-1; // 初值注意
            for(int i = 0; i < G[x].size(); i++) {
              Edge& e = edges[G[x][i]];
              if(e.cap > e.flow) m = min(m, d[e.to]);
            }
            if(--num[d[x]] == 0) break;
            num[d[x] = m+1]++;
            cur[x] = 0; // 注意
            if(x != s) x = edges[p[x]].from;
          }
        }
        return flow;
      }
    
      vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西
        BFS();///重新bfs一次
        vector<int> ans;
        for(int i = 0; i < edges.size(); i++) {
          Edge& e = edges[i];
          if(!vis[e.from] && vis[e.to] && e.cap > 0) {
            ans.push_back(i);
          }
        }
        return ans;
      }
    
      void Reduce() {
        for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
      }
    
      void print() {
        printf("Graph:
    ");
        for(int i = 0; i < edges.size(); i++)
          printf("%d->%d, %d, %d
    ", edges[i].from, edges[i].to , edges[i].cap, edges[i].flow);
      }
    };
    ISAP g;

    Dinic模板

    lrj的代码告诉我,不能随便的在Edge里面增加一个cost,因为这样在跑起来的时候会慢100ms(在UVA上就慢了100ms,更何况其他的)

    const int INF = 0x3f3f3f3f;
    struct Edge {
      int from, to, cap, flow;
    };
    
    struct Dinic {
      int n, m, s, t;        ///节点的个数,边的编号,起点,终点
      vector<Edge> edges;    // 边数的两倍
      vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
      bool vis[maxn];        // BFS使用
      int d[maxn];           // 从起点到i的距离
      int cur[maxn];         // 当前弧指针
      /////////蓝书363
      int inq[maxn];         // 是否在队列中
      int p[maxn];           // 上一条弧
      int a[maxn];           //可改进量
    
      void ClearAll(int n) {
        this->n = n;        ///这个赋值千万不能忘
        for(int i = 0; i < n; i++) G[i].clear();
        edges.clear();
      }
    
      void ClearFlow() {  ///清除流量,例如蓝书368的UVA11248里面的优化,就有通过清除流量来减少增广次数的
        for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
      }
    
      void Reduce() {///直接减少cap,也是减少增广次数的
        for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
      }
    
      void AddEdge(int from, int to, int cap) {
        edges.push_back((Edge){from, to, cap, 0});
        edges.push_back((Edge){to, from, 0, 0});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
      }
    
      bool BFS() {///bfs构建层次图
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(s);
        vis[s] = 1;
        d[s] = 0;
        while(!Q.empty()) {
          int x = Q.front(); Q.pop();
          for(int i = 0; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if(!vis[e.to] && e.cap > e.flow) {//只考虑残量网络中的弧
              vis[e.to] = 1;
              d[e.to] = d[x] + 1;
              Q.push(e.to);
            }
          }
        }
        return vis[t];
      }
    
      int DFS(int x, int a) {///a表示目前为止,所有弧的最小残量。但是也可以用它来限制最大流量,例如蓝书368LA2957,利用a来保证增量,使得最后maxflow=k
        if(x == t || a == 0) return a;
        int flow = 0, f;
        for(int& i = cur[x]; i < G[x].size(); i++) {//从上次考虑的弧,即已经访问过的就不需要在访问了
          Edge& e = edges[G[x][i]];
          if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
            e.flow += f;
            edges[G[x][i]^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;//如果不在这里终止,效率会大打折扣
          }
        }
        return flow;
      }
        /**最大流*/
      int Maxflow(int s, int t) {
        this->s = s; this->t = t;
        int flow = 0;
        while(BFS()) {
          memset(cur, 0, sizeof(cur));
          flow += DFS(s, INF);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流
        }
        return flow;
      }
    
      /**指定流量的最大流*/
      /*这里的limit表示离上限还有多少距离
      int Maxflow(int s, int t, int limit) {
        this->s = s; this->t = t;
        int flow = 0;
        while(BFS()) {
          memset(cur, 0, sizeof(cur));
          flow += DFS(s, limit - flow);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流
          if(flow == limit) break;
        }
        return flow;
      }
      */
        /**最小割*/
      vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西
        BFS();///重新bfs一次
        vector<int> ans;
        for(int i = 0; i < edges.size(); i++) {
          Edge& e = edges[i];
          if(vis[e.from] && !vis[e.to] && e.cap > 0) {///这里和ISAP不一样
            ans.push_back(i);
          }
        }
        return ans;
      }
    
      void debug(){///debug
        for (int i = 0; i < edges.size(); i++){
            printf("u = %d v = %d cap = %d flow = %d
    ", edges[i].from + 1, edges[i].to + 1, edges[i].cap, edges[i].flow);
        }
      }
    };
    Dinic g;

    Dinit+最小费用流模板(暂时,功能和注释需要不断加强,然后慢慢变得逐渐完整了,既可以求最大流,也可以求最小费用)

    const int INF = 0x3f3f3f3f;
    struct Edge {
      int from, to, cap, flow, cost;
      Edge(int f = 0, int t = 0, int c = 0, int fo = 0, int cost = 0): from(f), to(t), cap(c), flow(fo), cost(cost){}
    };
    
    struct Dinic {
      int n, m, s, t;        ///节点的个数,边的编号,起点,终点
      vector<Edge> edges;    // 边数的两倍
      vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
      bool vis[maxn];        // BFS使用
      int d[maxn];           // 从起点到i的距离
      int cur[maxn];         // 当前弧指针
      /////////蓝书363
      int inq[maxn];         // 是否在队列中
      int p[maxn];           // 上一条弧
      int a[maxn];           //可改进量
    
      void ClearAll(int n) {
        this->n = n;        ///这个赋值千万不能忘
        for(int i = 0; i < n; i++) G[i].clear();
        edges.clear();
      }
    
      void ClearFlow() {  ///清除流量,例如蓝书368的UVA11248里面的优化,就有通过清除流量来减少增广次数的
        for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
      }
    
      void Reduce() {///直接减少cap,也是减少增广次数的
        for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
      }
    
      void AddEdge(int from, int to, int cap, int cost = 0) {
        edges.push_back((Edge){from, to, cap, 0, cost});
        edges.push_back((Edge){to, from, 0, 0, -cost});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
      }
    
      bool BFS() {///bfs构建层次图
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(s);
        vis[s] = 1;
        d[s] = 0;
        while(!Q.empty()) {
          int x = Q.front(); Q.pop();
          for(int i = 0; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if(!vis[e.to] && e.cap > e.flow) {//只考虑残量网络中的弧
              vis[e.to] = 1;
              d[e.to] = d[x] + 1;
              Q.push(e.to);
            }
          }
        }
        return vis[t];
      }
    
      int DFS(int x, int a) {///a表示目前为止,所有弧的最小残量。但是也可以用它来限制最大流量,例如蓝书368LA2957,利用a来保证增量,使得最后maxflow=k
        if(x == t || a == 0) return a;
        int flow = 0, f;
        for(int& i = cur[x]; i < G[x].size(); i++) {//从上次考虑的弧,即已经访问过的就不需要在访问了
          Edge& e = edges[G[x][i]];
          if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) {
            e.flow += f;
            edges[G[x][i]^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;//如果不在这里终止,效率会大打折扣
          }
        }
        return flow;
      }
        /**最大流*/
      int Maxflow(int s, int t) {
        this->s = s; this->t = t;
        int flow = 0;
        while(BFS()) {
          memset(cur, 0, sizeof(cur));
          flow += DFS(s, INF);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流
        }
        return flow;
      }
    
      /**指定流量的最大流*/
      /*这里的limit表示离上限还有多少距离
      int Maxflow(int s, int t, int limit) {
        this->s = s; this->t = t;
        int flow = 0;
        while(BFS()) {
          memset(cur, 0, sizeof(cur));
          flow += DFS(s, limit - flow);///这里的INF可以发生改变,因为INF保证的是最大残量。但是也可以通过控制残量来控制最大流
          if(flow == limit) break;
        }
        return flow;
      }
      */
        /**最小割*/
      vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西
        BFS();///重新bfs一次
        vector<int> ans;
        for(int i = 0; i < edges.size(); i++) {
          Edge& e = edges[i];
          if(vis[e.from] && !vis[e.to] && e.cap > 0) {///这里和ISAP不一样
            ans.push_back(i);
          }
        }
        return ans;
      }
      /***************////以下是最小费用流算法
      bool BellmanFord(int s, int t, int &flow, int &cost) {///当然,cost是LL,也要记得修改
        this->s = s; this->t = t;
        for(int i = 0; i < n; i++) d[i] = INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
    
        queue<int> Q;
        Q.push(s);
        while(!Q.empty()) {
          int u = Q.front(); Q.pop();
          inq[u] = 0;
          for(int i = 0; i < G[u].size(); i++) {
            Edge& e = edges[G[u][i]];
            if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
              d[e.to] = d[u] + e.cost;
              p[e.to] = G[u][i];
              a[e.to] = min(a[u], e.cap - e.flow);
              if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
            }
          }
        }
    
        if(d[t] == INF) return false;///这里的条件看情况的
        flow += a[t];
        cost += d[t] * a[t];
        int u = t;
        while(u != s) {
          edges[p[u]].flow += a[t];
          edges[p[u]^1].flow -= a[t];
          u = edges[p[u]].from;
        }
        return true;
      }
    
      /// 需要保证初始网络中没有负权圈(属于调用最小费用流算法)
      int Mincost(int s, int t) {
        int flow = 0, cost = 0;
        while(BellmanFord(s, t, flow, cost)); ///这里的flow是看情况的
        return cost;
      }
    
      void debug(){///debug
        for (int i = 0; i < edges.size(); i++){
            printf("u = %d v = %d cap = %d flow = %d
    ", edges[i].from + 1, edges[i].to + 1, edges[i].cap, edges[i].flow);
        }
      }
    };
    Dinic g;

    ISAP + 最小费用流 

    const int INF = 0x3f3f3f3f;
    struct Edge {
      int from, to, cap, flow, cost;
      Edge(int f = 0, int t = 0, int c = 0, int fo = 0, int cost = 0): from(f), to(t), cap(c), flow(fo), cost(cost){}
    };
    
    struct ISAP {
      int n, m, s, t;
      vector<Edge> edges;
      vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
      bool vis[maxn];        // BFS使用
      int d[maxn];           // 从起点到i的距离
      int cur[maxn];        // 当前弧指针
      int p[maxn];          // 可增广路上的上一条弧
      int num[maxn];        // 距离标号计数
      /////////蓝书363
      int inq[maxn];         // 是否在队列中
      int a[maxn];           //可改进量
    
      void AddEdge(int from, int to, int cap, int cost = 0) {
        edges.push_back((Edge){from, to, cap, 0, cost});
        edges.push_back((Edge){to, from, 0, 0, -cost});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
      }
    
      bool BFS() {
        memset(vis, 0, sizeof(vis));
        queue<int> Q;
        Q.push(t);
        vis[t] = 1;
        d[t] = 0;
        while(!Q.empty()) {
          int x = Q.front(); Q.pop();
          for(int i = 0; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]^1];///因为t本来就是最后一个结点,一般最后都是反向边的?
            if(!vis[e.from] && e.cap > e.flow) {
              vis[e.from] = 1;
              d[e.from] = d[x] + 1;
              Q.push(e.from);
            }
          }
        }
        return vis[s];
      }
    
      void ClearAll(int n) {  ///为了重新建边
        this->n = n;
        for(int i = 0; i < n; i++) G[i].clear();
        edges.clear();
      }
    
      void ClearFlow() {  ///清除流量
        for(int i = 0; i < edges.size(); i++) edges[i].flow = 0;
      }
    
      int Augment() {  ///沿着增广边扩展flow
        int x = t, a = INF;
        while(x != s) {
          Edge& e = edges[p[x]];
          a = min(a, e.cap-e.flow);
          x = edges[p[x]].from;
        }
        x = t;
        while(x != s) {
          edges[p[x]].flow += a;
          edges[p[x]^1].flow -= a;
          x = edges[p[x]].from;
        }
        return a;
      }
    
      int Maxflow(int s, int t) {  ///求最大流
        this->s = s; this->t = t;
        int flow = 0;
        BFS();
        memset(num, 0, sizeof(num));
        for(int i = 0; i < n; i++) num[d[i]]++;
        int x = s;
        memset(cur, 0, sizeof(cur));
        while(d[s] < n) {
          if(x == t) {
            flow += Augment();
            x = s;
          }
          int ok = 0;
          for(int i = cur[x]; i < G[x].size(); i++) {
            Edge& e = edges[G[x][i]];
            if(e.cap > e.flow && d[x] == d[e.to] + 1) { // Advance
              ok = 1;
              p[e.to] = G[x][i];
              cur[x] = i; // 注意
              x = e.to;
              break;
            }
          }
          if(!ok) { /// Retreat 重新对待。因为该节点在目前的d下已经不存在增广路了,所以我们要对他进行增广
            int m = n-1; // 初值注意
            for(int i = 0; i < G[x].size(); i++) {
              Edge& e = edges[G[x][i]];
              if(e.cap > e.flow) m = min(m, d[e.to]);
            }
            if(--num[d[x]] == 0) break;
            num[d[x] = m+1]++;
            cur[x] = 0; // 注意
            if(x != s) x = edges[p[x]].from;
          }
        }
        return flow;
      }
    
      vector<int> Mincut() { /// call this after maxflow求最小割,就是S和T中还存在流量的东西
        BFS();///重新bfs一次
        vector<int> ans;
        for(int i = 0; i < edges.size(); i++) {
          Edge& e = edges[i];
          if(!vis[e.from] && vis[e.to] && e.cap > 0) {
            ans.push_back(i);
          }
        }
        return ans;
      }
    
      void Reduce() {
        for(int i = 0; i < edges.size(); i++) edges[i].cap -= edges[i].flow;
      }
    
      /***************////以下是最小费用流算法
      bool BellmanFord(int s, int t, int &flow, int &cost) {///当然,cost是LL,也要记得修改
        for(int i = 0; i < n; i++) d[i] = INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
    
        queue<int> Q;
        Q.push(s);
        while(!Q.empty()) {
          int u = Q.front(); Q.pop();
          inq[u] = 0;
          for(int i = 0; i < G[u].size(); i++) {
            Edge& e = edges[G[u][i]];
            if(e.cap > e.flow && d[e.to] > d[u] + e.cost) {
              d[e.to] = d[u] + e.cost;
              p[e.to] = G[u][i];
              a[e.to] = min(a[u], e.cap - e.flow);
              if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; }
            }
          }
        }
    
        if(d[t] == INF) return false;///这里的条件看情况的
        flow += a[t];
        cost += d[t] * a[t];
        int u = t;
        while(u != s) {
          edges[p[u]].flow += a[t];
          edges[p[u]^1].flow -= a[t];
          u = edges[p[u]].from;
        }
        return true;
      }
    
      /// 需要保证初始网络中没有负权圈(属于调用最小费用流算法)
      int Mincost(int s, int t) {
        int flow = 0, cost = 0;
        while(BellmanFord(s, t, flow, cost)); ///这里的flow是看情况的
        return cost;
      }
    
      void print() {
        printf("Graph:
    ");
        for(int i = 0; i < edges.size(); i++)
          printf("%d->%d, %d, %d
    ", edges[i].from, edges[i].to , edges[i].cap, edges[i].flow);
      }
    };
    ISAP g;
    View Code

    //**********************************************************************************************************************************************************//

    接下来开始探寻用head、next的建图方式了(垃圾POJ,毁我模板TAT,T我一脸)

  • 相关阅读:
    【LeetCode-树】找树左下角的值
    【LeetCode-贪心】合并区间
    MongoDB3.6版本新增特性
    MongoDB initial sync过程
    MongoDB3.4版本新增特性
    保险配置原则
    MongoDB批量操作时字段为null时没有入库
    Kafka消费者没有收到通知的分析
    Git分支的管理
    NoSQLBooster如何MongoDB的部分文档从一个集合拷贝到另外一个集合中
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5899746.html
Copyright © 2011-2022 走看看