• 网络流最小路径覆盖


    思路:

    http://blog.csdn.net/tramp_1/article/details/52742572

    每个点x拆成两个点x和x',分别表示x作为前驱和作为后继。若原图中x和y有边,向x和y'加一条有向边。如此构成二分图,记此二分图中作为前驱的节点集合为A,作为后继的节点集合为B。跑最大匹配,没有匹配的点的个数(n-最大匹配数)就是需要的最少的路径条数。正确性:二分匹配可以保证每个点顶多只有一个前驱,并且顶多只有一个后继,也就保证了每个点在且仅在一条路径中。此外,在二分图中,A中每个没有匹配的顶点对应了一条路径的终点。(类似地,B中每一个没有匹配的顶点对应了一条路径的起点。)最大二分匹配可以保证A(或B)中没有匹配的点的数量最少,亦即路径条数最少。

    实现:

      1 #include <bits/stdc++.h>
      2 
      3 #define N (1000 + 2)
      4 #define M (N * N + 4 * N)
      5 
      6 typedef long long LL;
      7 
      8 using namespace std;
      9 
     10 struct edge 
     11 {
     12     int v, cap, next;
     13 };
     14 edge e[M];
     15 
     16 int head[N], level[N], cur[N];
     17 int num_of_edges;
     18 
     19 /*
     20  * When there are multiple test sets, you need to re-initialize before each
     21  */
     22 void dinic_init(void) 
     23 {
     24     num_of_edges = 0;
     25     memset(head, -1, sizeof(head));
     26     return;
     27 }
     28 
     29 int add_edge(int u, int v, int c1, int c2) 
     30 {
     31     int& i = num_of_edges;
     32 
     33     assert(c1 >= 0 && c2 >= 0 && c1 + c2 >= 0); // check for possibility of overflow
     34     e[i].v = v;
     35     e[i].cap = c1;
     36     e[i].next = head[u];
     37     head[u] = i++;
     38 
     39     e[i].v = u;
     40     e[i].cap = c2;
     41     e[i].next = head[v];
     42     head[v] = i++;
     43     return i;
     44 }
     45 
     46 void print_graph(int n) 
     47 {
     48     for (int u=0; u<n; u++) 
     49     {
     50         printf("%d: ", u);
     51         for (int i=head[u]; i>=0; i=e[i].next) 
     52         {
     53             printf("%d(%d)", e[i].v, e[i].cap);
     54         }
     55         printf("
    ");
     56     }
     57     return;
     58 }
     59 
     60 /*
     61  * Find all augmentation paths in the current level graph
     62  * This is the recursive version
     63  */
     64 int dfs(int u, int t, int bn) 
     65 {
     66     if (u == t) return bn;
     67     int left = bn;
     68     for (int &i=cur[u]; i>=0; i=e[i].next) 
     69     {
     70         int v = e[i].v;
     71         int c = e[i].cap;
     72         if (c > 0 && level[u]+1 == level[v]) 
     73         {
     74             int flow = dfs(v, t, min(left, c));
     75             if (flow > 0) 
     76             {
     77                 e[i].cap -= flow;
     78                 e[i^1].cap += flow;
     79                 cur[u] = i;
     80                 left -= flow;
     81                 if (!left) break;
     82             }
     83         }
     84     }
     85     if (left > 0) level[u] = 0;
     86     return bn - left;
     87 }
     88 
     89 bool bfs(int s, int t) 
     90 {
     91     memset(level, 0, sizeof(level));
     92     level[s] = 1;
     93     queue<int> q;
     94     q.push(s);
     95     while (!q.empty()) 
     96     {
     97         int u = q.front();
     98         q.pop();
     99         if (u == t) return true;
    100         for (int i=head[u]; i>=0; i=e[i].next) 
    101         {
    102             int v = e[i].v;
    103             if (!level[v] && e[i].cap > 0) 
    104             {
    105                 level[v] = level[u]+1;
    106                 q.push(v);
    107             }
    108         }
    109     }
    110     return false;
    111 }
    112 
    113 LL dinic(int s, int t) 
    114 {
    115     LL max_flow = 0;
    116 
    117     while (bfs(s, t)) 
    118     {
    119         memcpy(cur, head, sizeof(head));
    120         max_flow += dfs(s, t, INT_MAX);
    121     }
    122     return max_flow;
    123 }
    124 
    125 int main() 
    126 {
    127     dinic_init();
    128     int m, n, x, y;
    129     cin >> m >> n;
    130     for (int i = 0; i < m; i++)
    131     {
    132         cin >> x >> y;
    133         add_edge(x, y + n, 1, 0);
    134     }
    135     for (int i = 1; i <= n; i++)
    136     {
    137         add_edge(0, i, 1, 0);
    138         add_edge(i + n, 2 * n + 1, 1, 0);
    139     }
    140     int ans = dinic(0, 2 * n + 1);
    141     cout << n - ans << endl;
    142     return 0;
    143 }
  • 相关阅读:
    java 基本数据类型的取值范围
    警惕自增的陷阱
    三元操作符的类型务必一致
    不要随便设置随机种子
    优先使用整形池
    IN、ANY、ALL与SOME
    第六章-序列:字符串、列表和元组 笔记
    第十二章-安全性
    第五章-数字 课后答案
    第十一章-约束、视图与事务
  • 原文地址:https://www.cnblogs.com/wangyiming/p/8213983.html
Copyright © 2020-2023  润新知