zoukankan      html  css  js  c++  java
  • NOIp 2018 前的图论板子总结

    存图

    存边

    直接开一个结构体数组存边

    struct Edge {
      int begin, end, weight;
    } edge[10010];
    int edge_count;
    inline void AddEdge(const int &u, const int &v, const int &w) {
      edge[edge_count++] = Edge {u, v, w};
    }
    

    应用:

    1. Kruskal's algorithm

    Adjacency matrix

    用二维数组adj[i][j]表示(i)(j)的关系

    int adj[1010][1010];
    #define ADD_EDGE(u, v, w) adj[u][v] = w
    

    应用:

    1. Floyd-Warshall algorithm
    2. Hangarian algorithm
    3. Kuhn-Munkres algorithm

    Adjacency list

    有几种形式, 以adj[i]表示以(i)为开头的边

    应用: 各种图论算法

    vector

    优点: 访问方便, 存图方便

    缺点: 消耗空间, 容易(MLE); 删边速度慢

    struct Edge {
      int destination, weight;
    };
    std::vector<Edge> adj[1010];
    
    加边
    #define ADD_EDGE(u, v, w) adj[u].push_back(Edge {v, w})
    
    访问
    for (register int i(0); i < adj[u].size(); ++i) {
      adj[u][i]...
      ...
    }
    

    for (auto i : adj[u]) {
      i...
      ...
    }
    
    list

    优点: 添边删边速度快

    缺点: 不易访问; 容易(MLE)

    struct Edge {
      int destination, weight;
    };
    std::list<Edge> adj[1010];
    
    加边
    #define ADD_EDGE(u, v, w) adj[u].push_back(Edge {v, w})
    
    访问
    for (register std::list<Edge>::iterator i = adj[u].begin(); i != adj[u].end(); ++i) {
      *i...
      ...
    }
    

    for (auto i : adj[u]) {
      i...
      ...
    }
    

    链式前向星

    优点: 空间重复利用, 通常不会(MLE), 而且很快

    缺点: 开小了会(WA), 开大了会(TLE), 有时还会(RE)

    应用: 各种图论算法

    struct Edge {
      int destination, weight, next;
    } edge[10010];
    int head[1010], edge_count;
    
    加边
    inline void AddEdge(const int &u, const int &v, const int &w) {
      edge[edge_count] = Edge {v, w, head[u]},
      head[u] = edge_count++;
    }
    
    访问
    for (register int i(head[u]); i != -1; i = edge[i].next) {
      edge[i]...
      ...
    }
    

    最小生成树

    题目链接: Luogu P3366 【模板】最小生成树

    Kruskal's algorithm

    将边以权值从小到大排序遍历, 用并查集加边, 同时维护答案。

    特点: 适合稀疏图

    时间复杂度: (Theta(|E| lg |V|))

    #include <cstdio>
    #include <algorithm>
    struct Edge {
      int begin, end, weight;
      inline bool operator <(const Edge &another) const {
        return this->weight < another.weight;
      }
    } edge[200010];
    int unions[5010];
    int n, m;
    inline int Find(const int&);
    inline int Kruskal();
    int main(int argc, char const *argv[]) {
      scanf("%d %d", &n, &m);
      for (register int i(0); i <= n; ++i) {
        unions[i] = i;
      }
      for (register int i(0), u, v, w; i < m; ++i) {
        scanf("%d %d %d", &u, &v, &w),
        edge[i] = Edge {u, v, w};
      }
      register int ans(Kruskal());
      if (ans) printf("%d
    ", ans);
      else puts("orz");
      return 0;
    }
    inline int Find(const int &x) {
      return unions[x] == x ? x : (unions[x] = Find(unions[x]));
    }
    inline int Kruskal() {
      register int ret(0);
      std::sort(edge, edge + m);
      for (register int i(0), countt(0), u, v; i < m; ++i) {
        u = Find(edge[i].begin), v = Find(edge[i].end);
        if (u != v) {
          unions[v] = u,
          ++countt,
          ret += edge[i].weight;
          if (countt == n - 1) {
            return ret;
          }
        }
      }
      return 0;
    }
    

    Prim's algorithm

    随机选择一个结点作为树, 每次找与这棵树相连且不构成环的最小边加入进来, 形成生成树。

    可以使用优先队列优化。

    特点: 适合稠密图

    时间复杂度: (Theta(|E| lg |V|))

    #include <cstdio>
    #include <queue>
    #include <vector>
    #include <cstring>
    struct Edge {
      int destination, weight;
    };
    std::vector<Edge> adj[5010];
    int distance[5010], vis[5010];
    int n, m;
    inline int Prim();
    int main(int argc, char const *argv[]) {
      memset(distance, 0x3f, sizeof(distance));
      scanf("%d %d", &n, &m);
      for (register int i(0), u, v, w; i < m; ++i) {
        scanf("%d %d %d", &u, &v, &w);
        adj[u].push_back(Edge {v, w}),
        adj[v].push_back(Edge {u, w});
      }
      register int ans(Prim());
      if (ans) printf("%d
    ", ans);
      else puts("orz");
      return 0;
    }
    inline int Prim() {
      register int ret(0), countt(0);
      distance[1] = 0;
      std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >, std::greater<std::pair<int, int> > > Q;
      Q.push(std::pair<int, int>(0, 1));
      while (!Q.empty()) {
        register int w(Q.top().first), u(Q.top().second);
        Q.pop();
        if (!vis[u]) {
          ++countt,
          ret += w,
          vis[u] = 1;
          for (auto i : adj[u]) {
            if (i.weight < distance[i.destination]) {
              distance[i.destination] = i.weight, Q.push(std::pair<int, int>(distance[i.destination], i.destination));
            }
          }
        }
      }
      return ret * (countt == n);
    }
    

    强连通分量

    题目链接: USACO 06 Jan. The Cow Prom

    Tarjan's strongly connected components algorithm

    是基于对图深度优先搜索(DFS)的算法, 每个强连通分量为搜索树中的一棵子树。搜索时, 把当前搜索树中未处理的节点加入一个堆栈, 回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

    时间复杂度: (Theta(|V|+|E|))

    #include <cstdio>
    #include <vector>
    #include <stack>
    #include <algorithm>
    std::vector<int> adj[10010];
    int dfn[10010], low[10010], color_count[10010], indexx, countt;
    bool instack[10010];
    int n, m, ans;
    std::stack<int> S;
    inline int Tarjan(const int&);
    int main(int argc, char const *argv[]) {
      scanf("%d %d", &n, &m);
      for (register int i(0), u, v;i < m; ++i) {
        scanf("%d %d", &u, &v);
        adj[u].push_back(v);
      }
      for (register int i(1); i <= n; ++i) {
        if (!dfn[i]) {
          Tarjan(i);
        }
      }
      for (register int i(1); i <= countt; ++i) {
        if (color_count[i] > 1) ++ans;
      }
      printf("%d
    ", ans);
      return 0;
    }
    inline void Tarjan(const int &cur) {
      dfn[cur] = low[cur] = ++indexx;
      S.push(cur), instack[cur] = 1;
      for (auto i : adj[cur]) {
        if (!dfn[i]) {
          Tarjan(i);
          low[cur] = std::min(low[cur], low[i]);
        } else if (instack[i]) {
            low[cur] = std::min(low[cur], low[i]);
        }
      }
      if (dfn[cur] == low[cur]) {
        ++countt;
        while (S.top() != cur) {
          register int node(S.top());
          S.pop(),
          instack[node] = 0,
          ++color_count[countt];
        }
        S.pop(),
        ++color_count[countt],
        instack[cur] = 0;
      }
    }
    

    二分图最大匹配

    题目链接: Luogu P3386 【模板】二分图匹配

    Hungarian algorithm

    通过寻找增广路来计算最大匹配值

    时间复杂度:

    1. Adjacency matrix: (Theta(n^3))
    2. Adjacency list: (Theta(nm))
    #include <cstdio>
    #include <vector>
    #include <cstring>
    std::vector<int> adj[1010];
    int n, m, e;
    int vis[1010], mate[1010];
    inline bool Match(const int&);
    inline int Hungarian();
    int main(int argc, char const *argv[]) {
      scanf("%d %d %d", &n, &m ,&e);
      for (register int i(0), u, v; i < e; ++i) {
        scanf("%d %d", &u, &v);
        if (u <= n && v <= m) {
          adj[u].push_back(v);
        }
      }
      printf("%d
    ", Hungarian());
      return 0;
    }
    inline int Hungarian() {
      register int ret(0);
      for (register int i(1); i <= n; ++i) {
        memset(vis, 0, sizeof(vis));
        ret += Match(i);
      }
      return ret;
    }
    inline bool Match(const int &cur) {
      for (auto i : adj[cur]) {
        if (!vis[i]) {
          vis[i] = 1;
          if (!mate[i] || Match(mate[i])) {
            mate[i] = cur;
            return true;
          }
        }
      }
      return false;
    }
    

    二分图最佳匹配

    Kuhn-Munkres algorithm

    是一种逐次修改可行顶标的方法,使之对应的等价子图逐次增广(增加边),最后出现完备匹配.

    时间复杂度: (Theta(n^3))

    #include <cstdio>
    #include <cstring>
    int nl, nr;
    int adj[310][310];
    int mate[310], dist_l[310], dist_r[310];
    int slack[310];
    bool vis_l[310], vis_r[310];
    int n;
    inline bool BestMatch(const int&);
    inline int KuhnMunkres();
    int main(int argc, char const *argv[]) {
      while(~scanf("%d", &n)) {
        for (register int i(0); i < n; ++i) {
          for(register int j(0); j < n; ++j) {
            scanf("%d", &adj[i][j]);
          }
        }
        nl = nr = n;
        printf("%d
    " ,KuhnMunkres());
      }
      return 0;
    }
    inline bool BestMatch(const int &cur) {
      vis_l[cur] = true;
      for (register int r(0); r < nr; ++r) {
        if (!vis_r[r]) {
          register int tmp(dist_l[cur] + dist_r[r] - adj[cur][r]);
          if (!tmp) {
            vis_r[r] = true;
            if(!~mate[r] || BestMatch(mate[r])){
              mate[r] = cur;
              return true;
            }
          }
          else if(slack[r] > tmp)
            slack[r] = tmp;
        }
      }
      return false;
    }
    inline int KuhnMunkres() {
      memset(mate, -1, sizeof(mate));
      memset(dist_r, 0, sizeof(dist_r));
      for (register int i(0); i < nl; ++i) {
        dist_l[i] = 0x80000000;
        for (register int j(0); j < nr; ++j) {
          if (adj[i][j] > dist_l[i]) {
            dist_l[i] = adj[i][j];
          }
        }
      }
      for (register int l(0); l < nl; ++l) {
        for (register int i(0); i < nr; ++i) {
          slack[i] = 0x7fffffff;
        }
        while(true) {
          memset(vis_l, false, sizeof vis_l ),
          memset(vis_r, false, sizeof vis_r );
          if (BestMatch(l)) break;
          register int d(0x7fffffff);
          for (register int i(0); i < nr; ++i){
            if (!vis_r[i] && d > slack[i]) {
              d = slack[i];
            }
          }
          for (register int i(0); i < nl; ++i){
            if (vis_l[i]) {
              dist_l[i] -= d;
            }
          }
          for (register int i(0); i < nr; ++i) {
            if (vis_r[i]) dist_r[i] += d;
            else slack[i] -= d;
          }
        }
      }
      register int res(0);
      for (register int i(0); i < nr; ++i) {
        if(~mate[i]) {
          res += adj[mate[i]][i];
        }
      }
      return res;
    }
    

    最近公共祖先

    Heavy path decomposition

    学倍增和Tarjan的时候没好好学现在只会树链剖分

    题目链接: Luogu P3379 【模板】最近公共祖先(LCA)

    时间复杂度:

    1. Dfs1: (Theta(n))
    2. Dfs2: (Theta(n))
    3. LowestCommonDivisor: (Theta(lg n))

    模板题要写读入优化过

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    int f(-1);
    inline char GetCharacter() {
        static char buf[2000000], *p1 = buf, *p2 = buf;
        return (p1 == p2) &&
               (p2 = (p1 = buf) + fread(buf, 1, 2000000, stdin), p1 == p2) ? 
               EOF : *p1++;
    }
    #define IS_DIGIT(c) (c >= '0' && c <= '9')
    inline void Read(int &x) {
        f = 1, x = 0;
        static char c = GetCharacter();
        while (!IS_DIGIT(c)) {
            if (c == '-') f = -1;
            c = GetCharacter();
        }
        while (IS_DIGIT(c)) x = x * 10 + c - '0', c = GetCharacter();
        x *= f;
    }
    #undef IS_DIGIT
    std::vector<int> adj[500010];
    int heavy[500010], size[500010], father[500010], top[500010], depth[500010];
    int root, n, m;
    int Dfs1(const int &cur, const int &fathernode) {
      size[cur] = 1;
      father[cur] = fathernode;
      depth[cur] = depth[fathernode] + 1;
      for (auto i : adj[cur]) {
        if (i != fathernode) {
          size[cur] += Dfs1(i, cur);
          if (size[i] > size[heavy[cur]]) heavy[cur] = i;
        }
      }
      return size[cur];
    }
    void Dfs2(const int &cur, const int &topnode) {
      top[cur] = topnode;
      if (heavy[cur]) {
        Dfs2(heavy[cur], topnode);
        for (auto i : adj[cur]) {
          if (heavy[cur] != i && father[cur] != i) {
            Dfs2(i, i);
          }
        }
      }
    }
    inline int LowestCommonAncestor(const int &x, const int &y) {
      int a(x), b(y);
      while (top[a] != top[b]) {
        if (depth[top[a]] < depth[top[b]]) std::swap(a, b);
        a = father[top[a]];
      }
      return depth[a] > depth[b] ? b : a;
    }
    int main(int argc, char const *argv[]) {
      Read(n), Read(m), Read(root);
      for (int i(1), u, v; i < n; ++i) {
        Read(u), Read(v);
        adj[u].push_back(v), adj[v].push_back(u);
      }
      Dfs1(root, root);
      Dfs2(root, root);
      while (m--) {
        int u, v;
        Read(u), Read(v);
        printf("%d
    ", LowestCommonAncestor(u, v));
      }
      return 0;
    }
    

    最短路

    Floyd-Warshall algorithm

    枚举每一个结点作为中间点, 再枚举每一个起点和终点, 可以松弛就进行松弛。

    时间复杂度: (Theta(n^3))

    题目链接: HDU 2544 最短路

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    int adj[110][110];
    int n, m;
    int main(int argc, char **argv) {
      while (~scanf("%d %d", &n, &m) && (n || m)) {
        memset(adj, 0x3f, sizeof(adj));
        for (register int i(1), u, v, w; i <= m; ++i) {
          scanf("%d %d %d", &u, &v, &w);
          adj[u][v] = adj[v][u] = std::min(adj[u][v], w);
        }
        for (register int i(1); i <= n; ++i) adj[i][i] = 0;
        for (register int k(1); k <= n; ++k) {
          for (register int i(1); i <= n; ++i) {
            for (register int j(1); j <= n; ++j) {
              adj[j][i] = adj[i][j] = std::min(adj[i][j], adj[i][k] + adj[k][j]);
            }
          }
        }
        printf("%d
    ", adj[1][n]);
      }
      return 0;
    }
    

    Shortest Path Faster Algorithm(SPFA)

    是Bellman-Ford algorithm的改进, 通常情况下不会出什么问题, 但精心设计的稠密图可以轻易卡掉SPFA, 使用需谨慎。

    平均时间复杂度: (Theta(|E|))

    理论上界: (Theta(|V||E|))

    题目链接: Luogu P3371 【模板】单源最短路径(弱化版)

    #include <deque>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    struct Edge {
      int destination, weight;
    };
    std::vector<Edge> adj[10010];
    int n, m, s;
    int distance[10010];
    bool vis[10010];
    inline void ShortestPathFasterAlgorithm();
    int main(int argc, char const *argv[]) {
      scanf("%d %d %d", &n, &m, &s);
      for (register int i(0), u, v, w; i < m; ++i) {
        scanf("%d %d %d", &u, &v, &w);
        adj[u].push_back(Edge {v, w});
      }
      ShortestPathFasterAlgorithm();
      for (register int i(1); i <= n; ++i) {
        printf("%d ", distance[i] == 0x3f3f3f3f ? 2147483647 : distance[i]);
      }
      return 0;
    }
    inline void ShortestPathFasterAlgorithm() {
      memset(distance, 0x3f, sizeof(distance));
      distance[s] = 0;
      std::deque<int> Q;
      Q.push_back(s);
      vis[s] = 0;
      while (!Q.empty()) {
        register int cur(Q.front());
        Q.pop_front();
        vis[cur] = 0;
        for (auto i : adj[cur]) {
          if (distance[i.destination] > distance[cur] + i.weight) {
            distance[i.destination] = distance[cur] + i.weight;
            if (!vis[i.destination]) {
              if (distance[i.destination] < distance[Q.front()]) Q.push_front(i.destination);
              else Q.push_back(i.destination);
              vis[i.destination] = 1;
            }
          }
        }
      }
    }
    

    Dijkstra's algorithm

    与最小生成树的Prim's algorithm相像, 主要思想为贪心, 适用于稠密图。

    通常情况下使用SPFA, 如果数据卡SPFA可以尝试堆优化的Dijkstra's algorithm。

    时间复杂度: (Theta((|E|)lg |E|))

    题目链接: Luogu P4779 【模板】单源最短路径(标准版)

    #include <cstdio>
    #include <vector>
    #include <queue>
    #include <cstring>
    std::vector<std::pair<int, long long> > adj[100010];
    long long distance[100010];
    int vis[100010];
    int n, m, s;
    inline void Dijkstra() {
      register std::priority_queue<std::pair<long long, int>, std::vector<std::pair<long long, int> >, std::greater<std::pair<long long, int> > > Q;
      memset(distance, 0x3f, sizeof(distance));
      distance[s] = 0;
      Q.push(std::pair<long long, int>(0ll, s));
      while (!Q.empty()) {
        register int cur(Q.top().second);
        Q.pop();
        if (!vis[cur]) {
          vis[cur] = 1;
          for (auto i : adj[cur]) {
            if (distance[i.first] > distance[cur] + i.second) {
              distance[i.first] = distance[cur] + i.second;
              Q.push(std::pair<long long, int>(distance[i.first], i.first));
            }
          }
        }
      }
    }
    int main(int argc, char const *argv[]) {
      scanf("%d %d %d", &n, &m, &s);
      for (register int i(1), u, v, w; i <= m; ++i) {
        scanf("%d %d %d", &u, &v, &w);
        adj[u].push_back(std::pair<int, long long>(v, (long long)(w)));
      }
      Dijkstra();
      for (register int i(1); i <= n; ++i) {
        printf(i == n ? "%lld
    " : "%lld ", distance[i]);
      }
      return 0;
    }
    

    最大流

    Edmonds-Karp algorithm

    最大流的基础方法, 思想非常简单:

    每次BFS找到一条增广路进行增广, 记录该路径上可增广的最大流, 在图中减去即可。、

    时间复杂度: (Theta(|V||E|^2))

    题目链接: USACO4.2 Drainage Ditches

    #include <cstdio>
    #include <queue>
    #include <vector>
    #include <algorithm>
    #include <cstring>
    #define INF 2147483647
    int predeccessor[210], flow[210];
    int G[210][210];
    int n, m;
    inline int Bfs(const int &S, const int &T) {
      register std::queue<int> q;
      memset(predeccessor, -1, sizeof(predeccessor));
      predeccessor[S] = 0;
      flow[S] = INF;
      q.push(S);
      while (!q.empty()) {
        register int current(q.front());
        q.pop();
        if (current == T) {
          break;
        }
        for (register int i(1); i <= n; ++i) {
          if (G[current][i] && predeccessor[i] == -1) {
            predeccessor[i] = current;
            flow[i] = std::min(flow[current], G[current][i]);
            q.push(i);
          }
        }
      }
      return predeccessor[T] == -1 ? -1 : flow[T];
    }
    inline int EdmondsKarp(const int &S, const int &T) {
      register int ret(0), increase(0);
      while((increase = Bfs(S, T)) != -1) {
        register int current(T);
        while (current != S) {
          G[predeccessor[current]][current] -= increase;
          G[current][predeccessor[current]] += increase;
          current = predeccessor[current];
        }
        ret += increase;
      }
      return ret;
    }
    int main(int argc, char **argv) {
      scanf("%d %d", &m, &n);
      for (register int i(0), u, v, w; i < m; ++i) {
        scanf("%d %d %d", &u, &v, &w);
        G[u][v] += w;
      }
      printf("%d
    ", EdmondsKarp(1, n));
      return 0;
    }
    

    Dinic's algorithm

    首先利用BFS对网络进行分层, 然后利用DFS从前一层向后一层反复寻找增广路(利用回溯), 当这次DFS无法继续增广时, 重复BFS步骤, 直到BFS无法到达汇点时, 算法结束。

    时间复杂度: (Theta(|V|^2 |E|))

    题目链接: Luogu P3376 【模板】网络最大流

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    int n, m, s, t;
    int depth[10010];
    struct Edge {
      int destination, maxflow, next;
      Edge() {
        destination = maxflow = next = 0;
      }
    } edge[200010];
    int head[10010];
    inline void AddEdge(const int &u, const int &v, const int &w) {
      static int edge_count(0);
      edge[edge_count].destination = v;
      edge[edge_count].maxflow = w;
      edge[edge_count].next = head[u];
      head[u] = edge_count++;
    }
    inline bool BreadthFirstSearch(const int &S, const int &T) {
      register std::queue<int> q;
      memset(depth, 0, sizeof(depth));
      depth[S] = 1;
      q.push(S);
      while (!q.empty()) {
        register int current(q.front());
        q.pop();
        for (register int i(head[current]); i != -1; i = edge[i].next) {
          if (edge[i].maxflow && !depth[edge[i].destination]) {
            depth[edge[i].destination] = depth[current] + 1;
            q.push(edge[i].destination);
          }
        }
      }
      return depth[T];
    }
    inline int DepthFirstSearch(const int &current, 
                                register int maxflow, 
                                const int &T) {
      if (current == T) return maxflow;
      for (register int i(head[current]); i != -1; i = edge[i].next) {
        if (depth[edge[i].destination] == depth[current] + 1 && edge[i].maxflow) {
          register int delta(DepthFirstSearch(edge[i].destination, 
                                              std::min(maxflow, edge[i].maxflow), 
                                              T));
          if (delta) {
            edge[i].maxflow -= delta;
            edge[i ^ 1].maxflow += delta;
            return delta;
          }
        }
      }
      return 0;
    }
    inline int Dinic(const int &S, const int &T) {
      register int ret(0), delta;
      while (BreadthFirstSearch(S, T)) {
        while ((delta = DepthFirstSearch(S, 0x3f3f3f3f, T))) {
          ret += delta;
        }
      }
      return ret;
    }
    int main(int argc, char **argv) {
      memset(head, -1, sizeof(head));
      scanf("%d %d %d %d", &n, &m, &s, &t);
      for (register int i(1), u, v, w; i <= m; ++i) {
        scanf("%d %d %d", &u, &v, &w);
        AddEdge(u, v, w), AddEdge(v, u, 0);
      }
      printf("%d
    ", Dinic(s, t));
      return 0;
    }
    

    最小费用最大流

    Dinic's algorithm

    将费用看作路径长度, 把Dinic's algorithm中的BFS换成SPFA, 每次找费用最小的进行增广

    时间复杂度: (Theta(|V|^2 |E|))

    题目链接: Luogu P3381 【模板】最小费用最大流

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <algorithm>
    int n, m, s, t;
    struct Edge {
      int destination, maxflow, cost, next;
      Edge() {
        destination = maxflow = cost = next = 0;
      }
    } edge[100010];
    int head[5010];
    int distance[5010], pre_node[5010], pre_edge[5010], flow[5010], vis[5010];
    int maxflow, mincost;
    inline void AddEdge(const int &u, const int &v, const int &w, const int &c) {
      static int edge_count(0);
      edge[edge_count].destination = v;
      edge[edge_count].maxflow = w;
      edge[edge_count].cost = c;
      edge[edge_count].next = head[u];
      head[u] = edge_count++;
    }
    inline bool ShortestPathFasterAlgorithm(const int &S, const int &T) {
      memset(distance, 0x3f, sizeof(distance));
      memset(flow, 0x3f, sizeof(flow));
      memset(vis, 0, sizeof(vis));
      register std::queue<int> q;
      q.push(S);
      vis[S] = 1, distance[S] = 0, pre_node[T] = -1;
      while (!q.empty()) {
        register int current(q.front());
        q.pop();
        vis[current] = 0;
        for (register int i(head[current]); i != -1; i = edge[i].next) {
          if (edge[i].maxflow && 
              distance[edge[i].destination] > distance[current] + edge[i].cost) {
            distance[edge[i].destination] = distance[current] + edge[i].cost;
            pre_node[edge[i].destination] = current;
            pre_edge[edge[i].destination] = i;
            flow[edge[i].destination] = std::min(flow[current], edge[i].maxflow);
            if (!vis[edge[i].destination]) {
              vis[edge[i].destination] = 1;
              q.push(edge[i].destination);
            }
          }
        }
      }
      return pre_node[T] != -1;
    }
    inline void Dinic(const int &S, const int &T) {
      while (ShortestPathFasterAlgorithm(S, T)) {
        register int current(T);
        maxflow += flow[T];
        mincost += flow[T] * distance[T];
        while (current != S) {
          edge[pre_edge[current]].maxflow -= flow[T];
          edge[pre_edge[current] ^ 1].maxflow += flow[T];
          current = pre_node[current];
        }
      }
    }
    int main(int argc, char **argv) {
      memset(head, -1, sizeof(head));
      scanf("%d %d %d %d", &n, &m, &s, &t);
      for (register int i(0), u, v, w, c; i < m; ++i) {
        scanf("%d %d %d %d", &u, &v, &w, &c);
        AddEdge(u, v, w, c), AddEdge(v, u, 0, -c);
      }
      Dinic(s, t);
      printf("%d %d
    ", maxflow, mincost);
      return 0;
    }
    
  • 相关阅读:
    冰蝎,从入门到魔改
    红蓝对抗——加密Webshell“冰蝎”攻防
    DGA域名的今生前世:缘起、检测、与发展
    DNS隐藏隧道的使用
    DPI (Deep Packet Inspection) 深度包检测技术
    中国菜刀原理
    一句话木马和中国菜刀的结合拿webshell
    十大黑客工具之一——中国菜刀
    十大ATT&CK攻击技战术
    防守方新秘籍:MITRE 发布主动防御指导框架Shield
  • 原文地址:https://www.cnblogs.com/forth/p/9733509.html
Copyright © 2011-2022 走看看