• 第11章例题(紫书)


    10/15

    这几天先专心刷一下图论的基础题目,也蛮多的,慢慢来。。。

    例题11-1 uva 12219

    题意:给你一个表达式,然后又一些子树在之前重复出现过,先要你给这些子树出现的顺序编个号1.。。N,然后如果重复出现就用编号替代,输出替代之后的表达式。

    题解:这是一个表达式树的问题,显示建树,如果让我来写的话,很麻烦,搞不好复杂度是O(n^2),因为字符串是一直扫描下去的,所以就利用一个指针作为全局,然后一直扫下去,就忽略一个左括号,建左树,然后忽略逗号,建右树,忽略右括号,然后一直扫下去,就可以在O(n)时间内把整棵树给建好。接下来就是怎么记录一颗子树,紫书上利用的是一个结构体(s,l,r)s代表这个串,其实如果比较串的话会很费时间,lrj做了一个处理,就是hash成一个值来存,l和r放的是这个树的编号,然后在map里面进行映射。

    这里我学到了不少技巧:hash减少复杂度,定义map的比较函数,利用建树的特点进行优化。。。。详见代码。

     

     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxn = 8e4 + 100;
     6 char str[maxn * 5], *p;
     7 int tcase, cnt, vis[maxn];
     8 
     9 struct Node {
    10   string s;
    11   int hash, left, right;
    12   bool operator < (const Node& a) const {
    13     if (hash != a.hash) return hash < a.hash;
    14     if (left != a.left) return left < a.left;
    15     return right < a.right;
    16   }
    17 };
    18 
    19 map<Node,int> mp;
    20 Node nodes[maxn];
    21 
    22 int build_tree() {
    23   int id = ++cnt;
    24   Node& temp = nodes[id];
    25   temp = (Node){"", 0, -1, -1};
    26   while (isalpha(*p)) {
    27     temp.hash = temp.hash * 27 + *p - '0' + 1;
    28     temp.s.push_back(*p); p++;
    29   }
    30   if ((*p) == '(') {
    31     p++;
    32     temp.left = build_tree();
    33     p++;
    34     temp.right = build_tree();
    35     p++;
    36   }
    37   if (mp.count(temp)) {
    38     --cnt;
    39     return mp[temp];
    40   }
    41   return mp[temp] = id;
    42 }
    43 
    44 void print(int u) {
    45   if (vis[u] == tcase) {
    46     printf("%d", mp[nodes[u]]);
    47     return;
    48   }
    49   cout << nodes[u].s;
    50   if (nodes[u].left != -1) {
    51     printf("(");
    52     print(nodes[u].left);
    53     printf(",");
    54     print(nodes[u].right);
    55     printf(")");
    56   }
    57   vis[u] = tcase;
    58 }
    59 
    60 int main() {
    61   //freopen("case.in", "r", stdin);
    62   int T;
    63   scanf("%d", &T);
    64   for (tcase = 1; tcase <= T; tcase++) {
    65     cnt = 0;
    66     mp.clear();
    67     scanf("%s", str);
    68     p = str;
    69     build_tree();
    70     print(1);
    71     puts("");
    72   }
    73   return 0;
    74 }
    代码君

     

    例题11-2 uva1395

    题意:给你一个图,让你选一个生成树使得生成树中的最大边和最小边的差值最小。

    题解:由于边数很小,所以暴力找即可。

     

     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxn = 8e4 + 100;
     6 char str[maxn], *p;
     7 int tcase, cnt, vis[maxn];
     8 
     9 struct Node {
    10   string s;
    11   int hash, left, right;
    12   bool operator < (const Node& a) const {
    13     if (hash != a.hash) return hash < a.hash;
    14     if (left != a.left) return left < a.left;
    15     return right < a.right;
    16   }
    17 };
    18 
    19 map<Node,int> mp;
    20 Node nodes[maxn];
    21 
    22 int build_tree() {
    23   int id = ++cnt;
    24   Node& temp = nodes[id];
    25   temp = (Node){"", 0, -1, -1};
    26   while (isalpha(*p)) {
    27     temp.hash = temp.hash * 27 + *p - '0' + 1;
    28     temp.s.push_back(*p); p++;
    29   }
    30   if ((*p) == '(') {
    31     p++;
    32     temp.left = build_tree();
    33     p++;
    34     temp.right = build_tree();
    35     p++;
    36   }
    37   if (mp.count(temp)) {
    38     --cnt;
    39     return mp[temp];
    40   }
    41   return mp[temp] = id;
    42 }
    43 
    44 void print(int u) {
    45   if (vis[u] == tcase) {
    46     printf("%d", mp[nodes[u]]);
    47     return;
    48   }
    49   cout << nodes[u].s;
    50   if (nodes[u].left != -1) {
    51     printf("(");
    52     print(nodes[u].left);
    53     printf(",");
    54     print(nodes[u].right);
    55     printf(")");
    56   }
    57   vis[u] = tcase;
    58 }
    59 
    60 int main() {
    61   //freopen("case.in", "r", stdin);
    62   int T;
    63   scanf("%d", &T);
    64   for (tcase = 1; tcase <= T; tcase++) {
    65     cnt = 0;
    66     mp.clear();
    67     scanf("%s", str);
    68     p = str;
    69     build_tree();
    70     print(1);
    71     puts("");
    72   }
    73   return 0;
    74 }
    代码君

     

    例题11-3 uva 1151

    题意:给你n个点,点与点的权值是他们的欧几里得距离的平方,然后给你q个套餐,花费为C[i],购买连接的点,也就是花费C[i]购买一些点使得这些点的权值为0,问你最小花费得到所有的点。

    题解:这里暴力枚举q个套餐,因为q只有8,所以只是1<<q,然后是边,我们只要考虑最小生成树中的边,因为当在套餐中,不在生成树中的边权值为0,然后会去掉的一定是生成树中的某一条边(画图易知),这样的复杂度就是(1<<q)n;我们只需要小球一次最小生成树,然后记录这里的所有边,然后就枚举套餐,把套餐的所有点都加进并查集,然后在把生成树中的点加进并查集,去一个最小值就是答案。

     

     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxm = 1e6 + 10;
     6 const int maxn = 1e3 + 10;
     7 const int maxq = 10;
     8 int n, q;
     9 int wi[maxq], fa[maxn], x[maxn], y[maxn];
    10 vector<int> tc[maxq];
    11 
    12 struct Edge {
    13   int u, v, w;
    14   bool operator < (const Edge& a) const {
    15     return w < a.w;
    16   }
    17 };
    18 
    19 Edge before[maxm];
    20 Edge after[maxm];
    21 
    22 int find_root(int rt) {
    23   if (rt == fa[rt]) return rt;
    24   return fa[rt] = find_root(fa[rt]);
    25 }
    26 
    27 int find_MST(int x, int need, bool flag = false) {
    28   if (need == 1) return 0;
    29   int ans = 0, cnt = 0;
    30   for (int i = 0; i < x; i++) {
    31     int fu = find_root(before[i].u);
    32     int fv = find_root(before[i].v);
    33     if (fu != fv) {
    34       fa[fu] = fv;
    35       ans += before[i].w;
    36       if (flag) after[cnt++] = before[i];
    37       if (--need == 1) break;
    38     }
    39   }
    40   return ans;
    41 }
    42 
    43 int main() {
    44   //freopen("case.in", "r", stdin);
    45   int T;
    46   scanf("%d", &T);
    47   while (T--) {
    48     scanf("%d%d", &n, &q);
    49     for (int i = 0; i < q; i++) {
    50       int x, t;
    51       scanf("%d%d", &x, wi + i);
    52       tc[i].clear();
    53       while (x--) {
    54         scanf("%d", &t);
    55         tc[i].push_back(t);
    56       }
    57     }
    58     for (int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]);
    59     int cnt = 0;
    60     for (int i = 1; i <= n; i++) {
    61       for (int j = i + 1; j <= n; j++) {
    62         before[cnt++] = (Edge){i, j, (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])};
    63       }
    64     }
    65 
    66     sort(before, before + cnt);
    67     for (int i = 1; i <= n; i++)  fa[i] = i;
    68     int ans = find_MST(cnt, n, true);
    69     memcpy(before, after, sizeof(Edge) * (n - 1));
    70 
    71     for (int i = 1; i < (1 << q); i++) {
    72       int now = n, sum = 0;
    73       for (int j = 1; j <= n; j++)  fa[j] = j;
    74       for (int j = 0; j < q; j++) if (i & (1 << j)) {
    75           sum += wi[j];
    76           int sz = tc[j].size();
    77           for (int k = 1; k < sz; k++) {
    78             int u = find_root(tc[j][k]), v = find_root(tc[j][0]);
    79             if (u != v) {
    80               now--; fa[u] = v;
    81             }
    82           }
    83       }
    84       ans = min(ans, find_MST(n - 1, now) + sum);
    85     }
    86     printf("%d
    ", ans);
    87     if (T)  puts("");
    88   }
    89   return 0;
    90 }
    代码君

     

    例题11-4 uva247

    题意:给你n个人,m条电话信息,表示u可以打电话给v,然后问你可以互相打电话的放在一个集合,让你输出所有集合。

    题解:经典题目,floyd找传递闭包,我们把floyd的递推式子改成G[i][j] = G[i][j] || G[i][k] && G[k][j]即可。具体原理,理解了floyd算法就很容易理解了。。。

     

     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxn = 30;
     6 int G[maxn][maxn], vis[maxn];
     7 string str[maxn];
     8 map<string,int> mp;
     9 
    10 int main() {
    11   //freopen("case.in", "r", stdin);
    12   int n, m, tcase = 0;
    13   while (scanf("%d%d", &n, &m) == 2 && (n + m)) {
    14     if (tcase) puts("");
    15     mp.clear();
    16     memset(G, 0, sizeof(G));
    17     string s1, s2;
    18     int id = 0, id1, id2;
    19     for (int i = 0; i < m; i++) {
    20       cin >> s1 >> s2;
    21       if (!mp.count(s1)) id1 = mp[s1] = ++id;
    22       else id1 = mp[s1];
    23       if (!mp.count(s2)) id2 = mp[s2] = ++id;
    24       else id2 = mp[s2];
    25       G[id1][id2] = 1;
    26       str[id1] = s1;
    27       str[id2] = s2;
    28     }
    29     for (int k = 1; k <= n; k++) {
    30       for (int i = 1; i <= n; i++) {
    31         for (int j = 1; j <= n; j++) {
    32           G[i][j] = G[i][j] || (G[i][k] && G[k][j]);
    33         }
    34       }
    35     }
    36     memset(vis, 0, sizeof vis);
    37     printf("Calling circles for data set %d:
    ", ++tcase);
    38     for (int i = 1; i <= n; i++) {
    39       if (vis[i]) continue;
    40       cout << str[i];
    41       for (int j = i + 1; j <= n; j++) {
    42         if (G[i][j] && G[j][i]) {
    43           cout << ", " << str[j];
    44           vis[j] = 1;
    45         }
    46       }
    47       puts("");
    48     }
    49   }
    50   return 0;
    51 }
    代码君

     

    例题11-5 uva 10048

    题意:给你一幅图,n个点,m条无向边的带权图,然后又q个询问,u和v,问你u到v的所有路径中最小的路径中最大权值是多少?

    题解:这题也是floyd算法的经典应用,只要floyd的原理是枚举可能的中间节点,同样,可能是G[i][k]或者是G[k][j]最长,所以只要max{ G[i][k], G[k][j] },然后取最小值即可,注意要i到k且k到j都有可行路径的时候才可以转移。

     

     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxn = 110;
     6 const int INF = 1e9;
     7 int G[maxn][maxn];
     8 
     9 int main() {
    10  // freopen("case.in", "r", stdin);
    11   int n, m, q, tcase = 0;
    12   while (scanf("%d%d%d", &n, &m, &q) == 3 && (n + m + q)) {
    13     if (tcase) puts("");
    14     for (int i = 1; i <= n; i++)
    15       for (int j = 1; j <= n; j++)
    16         G[i][j] = INF;
    17     for (int i = 0; i < m; i++) {
    18       int u, v, w;
    19       scanf("%d%d%d", &u, &v, &w);
    20       G[u][v] = G[v][u] = w;
    21     }
    22     for (int k = 1; k <= n; k++) {
    23       for (int i = 1; i <= n; i++) {
    24         for (int j = 1; j <= n; j++) {
    25           if (G[i][k] != INF && G[k][j] != INF)
    26             G[i][j] = min(G[i][j], max(G[i][k], G[k][j]));
    27         }
    28       }
    29     }
    30     printf("Case #%d
    ", ++tcase);
    31     while (q--) {
    32       int u, v;
    33       scanf("%d%d", &u, &v);
    34       if (G[u][v] != INF)
    35         printf("%d
    ", G[u][v]);
    36       else puts("no path");
    37     }
    38   }
    39   return 0;
    40 }
    代码君

     

    例题11-6 uva658

    题意:有n种bug,m个补丁,然后补丁的描述是给你一个时间ti,两个字符串s和t,ti表示这个bug的执行时间,s表示打补丁之前的bug状态,+表示这个位置一定要有bug,-表示这个位置一定要没有bug,然后0表示这个位置可以有可以没有,t表示这个bug打完补丁之后的状态,+表示打完之后这里会有bug,-表示这个这个位置的bug会消失,0表示维持不变。现在给你一个都是bug的状态,问你存不存在方案变成没有bug的状态,有的话最短时间是多少?

    题解:首先是怎样枚举状态,我们用1表示这个位置有bug,0表示这个位置没有bug,然后就是1<<n-1到0的最短路,如果直接把图建出来的话,节点数太多了,有很多没有用的节点,所以我们就边搜索边枚举,也就是当要扩展一个节点的时候就遍历每一个补丁,看有哪些状态会生成,然后在进行找最短路。可扩展的状态少,虽然节点数多,所以勉强可以跑出结果。

     

    /*zhen hao*/
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxm = 1e2 + 10;
    const int INF = 1e9;
    const int maxn = 1 << 20;
    int n, m;
    int d[maxn], done[maxn], digit[maxm];
    
    struct Node {
      int ti;
      string s, t;
    };
    
    Node nodes[maxn];
    
    struct HeapNode {
      int d, u;
      bool operator < (const HeapNode& rhs) const {
        return d > rhs.d;
      }
    };
    
    int get_v(int u, int& v, int id) {
      for (int i = n - 1; i >= 0; i--) {
        digit[i] = u % 2;
        u /= 2;
      }
      v = 0;
      for (int i = 0; i < n; i++) {
        if (nodes[id].s[i] == '+' && digit[i] != 1) return false;
        if (nodes[id].s[i] == '-' && digit[i] != 0) return false;
        if (nodes[id].t[i] == '+')  digit[i] = 1;
        if (nodes[id].t[i] == '-')  digit[i] = 0;
        v = v * 2 + digit[i];
      }
      return true;
    }
    
    void dijkstra(int s) {
      priority_queue<HeapNode> Q;
      for (int i = 0; i <= s; i++) d[i] = INF;
      d[s] = 0;
      memset(done, false, sizeof done);
      Q.push((HeapNode){0, s});
      while (!Q.empty()) {
        HeapNode x = Q.top(); Q.pop();
        int u = x.u;
        if (done[u])  continue;
        done[u] = true;
        for (int i = 0; i < m; i++) {
          int v;
          if (!get_v(u, v, i))  continue;
          if (d[v] > d[u] + nodes[i].ti) {
            d[v] = d[u] + nodes[i].ti;
            Q.push((HeapNode){d[v], v});
          }
        }
      }
    }
    
    int main() {
      //freopen("case.in", "r", stdin);
      int tcase = 0;
      while (scanf("%d%d", &n, &m) == 2 && (n + m)) {
        for (int i = 0; i < m; i++) {
          cin >> nodes[i].ti >> nodes[i].s >> nodes[i].t;
        }
        printf("Product %d
    ", ++tcase);
        dijkstra((1 << n) - 1);
        if (d[0] != INF)  printf("Fastest sequence takes %d seconds.
    
    ", d[0]);
        else puts("Bugs cannot be fixed.
    ");
      }
      return 0;
    }
    代码君(Me)
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn = 20;
     5 const int maxm = 110;
     6 const int inf = 1e9;
     7 int n, m, ti[maxm], done[1<<maxn], dist[1<<maxn];
     8 char before[maxm][maxn + 5], after[maxm][maxn + 5];
     9 
    10 struct Node {
    11   int u, d;
    12   bool operator < (const Node& a) const {
    13     return d > a.d;
    14   }
    15 };
    16 
    17 int dijkstra(int s) {
    18   for (int i = 0; i <= s; i++)  dist[i] = inf, done[i] = 0;
    19   priority_queue<Node> q;
    20   q.push((Node){s, 0});
    21   dist[s] = 0;
    22   while (!q.empty()) {
    23     Node x = q.top(); q.pop();
    24     int u = x.u;
    25     if (done[u])  continue;
    26     if (u == 0) return x.d;
    27     done[u] = 1;
    28     for (int i = 0; i < m; i++) {
    29       bool ok = true;
    30       for (int j = 0; j < n && ok; j++) {
    31         if (before[i][j] == '+' && !(u & (1 << j))) ok = false;
    32         if (before[i][j] == '-' && u & (1 << j)) ok = false;
    33       }
    34       if (!ok) continue;
    35       int v = u;
    36       for (int j = 0; j < n; j++) {
    37         if (after[i][j] == '+') v |= (1 << j);
    38         if (after[i][j] == '-') v &= ~(1 << j);
    39       }
    40       if (dist[v] > dist[u] + ti[i]) {
    41         dist[v] = dist[u] + ti[i];
    42         q.push((Node){v, dist[v]});
    43       }
    44     }
    45   }
    46   return -1;
    47 }
    48 
    49 int main() {
    50   //freopen("case.in", "r", stdin);
    51   int tcase = 0;
    52   while (scanf("%d%d", &n, &m) == 2 && (n + m)) {
    53     for (int i = 0; i < m; i++) scanf("%d%s%s", &ti[i], before[i], after[i]);
    54     int ans = dijkstra((1<<n) - 1);
    55     printf("Product %d
    ", ++tcase);
    56     if (ans < 0) puts("Bugs cannot be fixed.
    ");
    57     else printf("Fastest sequence takes %d seconds.
    
    ", ans);
    58   }
    59   return 0;
    60 }
    代码君(Lrj)

     

    例题11-7 uva 753

    题意:给你n个插座,m个设备,k个转换器,首先是m个设备有设备名字和插头类型组成,所以设备当然是插在插座上的,然后转换器就是插座->插头,因为设备的B插头可以查到B->C的转换器,然后变成C插头的,并且转换器可以用无限个,最后问你n个插座有多少个是没插任何设备的。

    题解:有两种做法;

    第一种是网络流的做法,首先是超级源点s连接着设备,表示可以转移到设备,实际上连的是插头,然后是插头连着转换器,如果插头a可以变成插头b,那么就是连一条边,流量为无穷,最后还对应的插头连着插座,流量为1,最后求一个最大流就代表有设备的插头的最大数,n-最大数就是答案。

     

      1 /*zhen hao*/
      2 #include <bits/stdc++.h>
      3 using namespace std;
      4 
      5 const int maxn = 410;
      6 const int INF = 1000;
      7 int n, m, k, cnt, vis[maxn];
      8 string str1[maxn], str2[maxn], str3[maxn], str4[maxn], t;
      9 map<string, int> mp;
     10 
     11 struct Edge {
     12   int from, to, cap, flow;
     13 };
     14 
     15 struct EK {
     16   int n, m;
     17   vector<Edge> edges;
     18   vector<int> G[maxn];
     19   int a[maxn];
     20   int p[maxn];
     21 
     22   void init(int n) {
     23     edges.clear();
     24     for (int i = 0; i < n; i++)  G[i].clear();
     25   }
     26 
     27   void add_edge(int from, int to, int cap) {
     28     edges.push_back((Edge){from, to, cap, 0});
     29     edges.push_back((Edge){to, from, 0, 0});
     30     m = edges.size();
     31     G[from].push_back(m - 2);
     32     G[to].push_back(m - 1);
     33   }
     34 
     35   int max_flow(int s, int t) {
     36     int flow = 0;
     37     for (;;) {
     38       memset(a, 0, sizeof a);
     39       queue<int> Q;
     40       Q.push(s);
     41       a[s] = INF;
     42       while (!Q.empty()) {
     43         int x = Q.front(); Q.pop();
     44         for (int i = 0; i < (int)G[x].size(); i++) {
     45           Edge& e = edges[G[x][i]];
     46           if (!a[e.to] && e.cap > e.flow) {
     47             p[e.to] = G[x][i];
     48             a[e.to] = min(a[x], e.cap - e.flow);
     49             Q.push(e.to);
     50           }
     51         }
     52         if (a[t]) break;
     53       }
     54       if (!a[t])  break;
     55       for (int u = t; u != s; u = edges[p[u]].from) {
     56         edges[p[u]].flow += a[t];
     57         edges[p[u] ^ 1].flow -= a[t];
     58       }
     59       flow += a[t];
     60     }
     61     return flow;
     62   }
     63 };
     64 
     65 EK ek;
     66 
     67 void init_graph() {
     68   mp.clear();
     69   memset(vis, 0, sizeof vis);
     70   ek.init(maxn);
     71   scanf("%d", &n);
     72   for (int i = 1; i <= n; i++)  cin >> str1[i];
     73   scanf("%d", &m);
     74   cnt = n;
     75   for (int i = 1; i <= m; i++) {
     76     cin >> t >> str2[i];
     77     if (!mp.count(str2[i])) mp[str2[i]] = ++cnt;
     78     vis[mp[str2[i]]]++;
     79   }
     80   scanf("%d", &k);
     81   for (int i = 1; i <= k; i++) {
     82     cin >> str3[i] >> str4[i];
     83     if (!mp.count(str3[i])) mp[str3[i]] = ++cnt;
     84     if (!mp.count(str4[i])) mp[str4[i]] = ++cnt;
     85   }
     86   ++cnt;
     87   for (int i = 1; i <= n; i++) {
     88     ek.add_edge(i, cnt, 1);
     89     if (!mp.count(str1[i])) continue;
     90     ek.add_edge(mp[str1[i]], i, 1);
     91   }
     92   for (int i = n + 1; i < cnt; i++) {
     93     if (!vis[i])  continue;
     94     ek.add_edge(0, i, vis[i]);
     95   }
     96   for (int i = 1; i <= k; i++) {
     97     ek.add_edge(mp[str3[i]], mp[str4[i]], INF);
     98   }
     99 }
    100 
    101 int main() {
    102   //freopen("case.in", "r", stdin);
    103   int T;
    104   scanf("%d", &T);
    105   while (T--) {
    106     init_graph();
    107     cout << m - ek.max_flow(0, cnt) << endl;
    108     if (T)  puts("");
    109   }
    110   return 0;
    111 }
    代码君

     

    第二种是floyd+二分图,我们先用floyd求出什么插头是相互转化的,然后用一个矩阵,x表示设备,y代表插座,因为同一设备不能连在同一个插座,所以最后求个最大匹配就是有设备的最大数,然后n-最大数就是答案。还要注意一个地方就是设备是可以连到原来的插头的,但是不存在对应这种插头的插座是不能连的,所以要注意连边。。。

     

     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxn = 510;
     6 int n, m, k, cnt, used[maxn], from[maxn];
     7 int G1[maxn][maxn], G2[maxn][maxn];
     8 string str1[maxn], t1, t2;
     9 map<string, int> mp;
    10 
    11 void floyd() {
    12   for (int k = 1; k <= cnt; k++)
    13     for (int i = 1; i <= cnt; i++)
    14       for (int j = 1; j <= cnt; j++)
    15         G1[i][j] = G1[i][j] || (G1[i][k] && G1[k][j]);
    16 }
    17 
    18 void init_graph() {
    19   memset(G1, 0, sizeof(G1));
    20   memset(G2, 0, sizeof(G2));
    21   mp.clear();
    22   scanf("%d", &n);
    23   for (int i = 1; i <= n; i++) cin >> t1, mp[t1] = i;
    24   cnt = n;
    25   scanf("%d", &m);
    26   for (int i = 1; i <= m; i++) {
    27     cin >> t1 >> str1[i];
    28     if (!mp.count(str1[i])) mp[str1[i]] = ++cnt;
    29   }
    30   scanf("%d", &k);
    31   for (int i = 1; i <= k; i++) {
    32     cin >> t1 >> t2;
    33     if (!mp.count(t1)) mp[t1] = ++cnt;
    34     if (!mp.count(t2)) mp[t2] = ++cnt;
    35     int x = mp[t1], y = mp[t2];
    36     G1[x][y] = 1;
    37   }
    38   floyd();
    39   for (int i = 1; i <= m; i++) {
    40     int id = mp[str1[i]];
    41     for (int j = 1; j <= n; j++)  G2[i][j] = G1[id][j];
    42     if (id <= n)  G2[i][i] = 1;
    43   }
    44 }
    45 
    46 bool is_match(int i) {
    47     for(int j = 1; j <= n; j++) {
    48       if(G2[i][j] && !used[j]) {
    49           used[j] = 1;
    50           if(from[j] == -1 || is_match(from[j])) {
    51               from[j] = i;
    52               return true;
    53           }
    54       }
    55     }
    56   return false;
    57 }
    58 
    59 int max_match() {
    60   int ret = 0;
    61   memset(from, -1, sizeof(from));
    62   for(int i = 1; i <= m; i++) {
    63       memset(used, 0, sizeof(used));
    64       if(is_match(i)) ret++;
    65   }
    66   return ret;
    67 }
    68 
    69 int main() {
    70   //freopen("case.in", "r", stdin);
    71   int T;
    72   scanf("%d", &T);
    73   while (T--) {
    74     init_graph();
    75     cout << m - max_match() << endl;;
    76     if (T)  puts("");
    77   }
    78   return 0;
    79 }
    代码君

     

    例题11-8 uva 11082

    题意:给你R x C的矩阵,然后给出R个数,代表前i行的所有数之和,给出C个数,代表前j列的所有数之和,保证有解,让你构造出这个矩阵,每个数的范围是1-20。

    题解:这里我们充分利用1-20这个条件,先把矩阵变成一个二分图,左边是行,右边是列,然后这是一个完全二分图,每一条边代表一个点(i,j)然后他们权值的范围还1-20,因为可能存在有些没有流,所以我们将每条边的流量限制为19,然后设置一个超级源点s到每一行,流量为r[i] - c,因为每个元素都减了一,然后就是设置一个超级汇点t,列元素流向t,流量限制为c[i] - r,最后求一个最大流之后,由于保证有解,所以最后结果一定是所有的∑c[i]-c,怎么找到解呢?记录每条列元素到行元素的编号,然后他们的流量 + 1就是答案。

     

      1 /*zhen hao*/
      2 #include <bits/stdc++.h>
      3 using namespace std;
      4 
      5 const int maxn = 100;
      6 const int inf = 1e9;
      7 int r[maxn], c[maxn], no[maxn][maxn];
      8 int R, C;
      9 
     10 struct Edge {
     11   int from, to, cap, flow;
     12 };
     13 
     14 struct EK {
     15   int n, m;
     16   vector<Edge> edges;
     17   vector<int> G[maxn];
     18   int a[maxn];
     19   int p[maxn];
     20 
     21   void init(int n) {
     22     for (int i = 0; i < n; i++) G[i].clear();
     23     edges.clear();
     24   }
     25 
     26   void add_edge(int u, int v, int cap) {
     27     edges.push_back((Edge){u, v, cap, 0});
     28     edges.push_back((Edge){v, u, 0, 0});
     29     m = edges.size();
     30     G[u].push_back(m - 2);
     31     G[v].push_back(m - 1);
     32   }
     33 
     34   int max_flow(int s, int t) {
     35     int flow = 0;
     36     for (;;) {
     37       memset(a, 0, sizeof a);
     38       queue<int> Q;
     39       Q.push(s);
     40       a[s] = inf;
     41       while (!Q.empty()) {
     42         int x = Q.front(); Q.pop();
     43         for (int i = 0; i < (int)G[x].size(); i++) {
     44           Edge& e = edges[G[x][i]];
     45           if (!a[e.to] && e.cap > e.flow) {
     46             p[e.to] = G[x][i];
     47             a[e.to] = min(a[x], e.cap - e.flow);
     48             Q.push(e.to);
     49           }
     50         }
     51         if (a[t]) break;
     52       }
     53       if (!a[t])  break;
     54       for (int u = t; u != s; u = edges[p[u]].from) {
     55         edges[p[u]].flow += a[t];
     56         edges[p[u] ^ 1].flow -= a[t];
     57       }
     58       flow += a[t];
     59     }
     60     return flow;
     61   }
     62 };
     63 
     64 EK ek;
     65 
     66 int main() {
     67   //freopen("case.in", "r", stdin);
     68   int T, tcase = 0;
     69   scanf("%d", &T);
     70   while (T--) {
     71     scanf("%d%d", &R, &C);
     72     for (int i = 1; i <= R; i++)  scanf("%d", &r[i]);
     73     for (int i = 1; i <= C; i++)  scanf("%d", &c[i]);
     74     for (int i = R; i > 1; i--)  r[i] -= r[i - 1];
     75     for (int i = C; i > 1; i--)  c[i] -= c[i - 1];
     76     ek.init(R + C + 2);
     77     for (int i = 1; i <= R; i++) {
     78       ek.add_edge(0, i, r[i] - C);
     79     }
     80     for (int i = 1; i <= R; i++) {
     81       for (int j = 1; j <= C; j++) {
     82         ek.add_edge(i, j + R, 19);
     83         no[i][j] = ek.edges.size() - 2;
     84       }
     85     }
     86     for (int i = 1; i <= C; i++) {
     87       ek.add_edge(i + R, R + C + 1, c[i] - R);
     88     }
     89     ek.max_flow(0, R + C + 1);
     90     printf("Matrix %d
    ", ++tcase);
     91     for (int i = 1; i <= R; i++) {
     92       for (int j = 1; j <= C; j++) {
     93         printf("%d ", ek.edges[no[i][j]].flow + 1);
     94       }
     95       puts("");
     96     }
     97     puts("");
     98   }
     99   return 0;
    100 }
    代码君

    例题11-9 uva 1658

    题意:给你一个有向带权图,n个点,e条边,然后让你找两条从1到n的路径,使得这两条路径之和最小,要求这两条路径不能有点重复访问。

    题解:这题用到了拆点法。首先如果单纯地求流量为2的最小费用会有问题,可能出现一个点访问两次,例如下图:

     

    X点被访问了两次,所以不满足条件,限制流量可以使得一条边只被访问一次,因为一条边的流量为1,不能限制一个点只被访问一次,所以要用拆点法,把一个点拆成两个点,然后这两个点相连,流量为1,费用为0,这样就能够避免一个点被访问两次,因为一个点i到自己的另外一个点i’只需要费用为0,所以一定会走到这条边,然后它所得到的流量为1,所以只能流向一条边,不可能流向两条边,所以上图中x点流向两条边是不可能的,所以就很好地避免了一个点被访问两次,紫书上提醒:解决结点流量的通用方法就是拆点,也就是为了限制一个结点的固定流量,要利用拆点法。

      1 /*zhen hao*/
      2 #include <bits/stdc++.h>
      3 using namespace std;
      4 
      5 typedef long long ll;
      6 const int maxn = 2e3 + 100;
      7 const int inf = 1e9;
      8 
      9 struct Edge {
     10   int from, to, cap, flow, cost;
     11 };
     12 
     13 struct MCMF {
     14   int n, m;
     15   vector<Edge> edges;
     16   vector<int> G[maxn];
     17   int inq[maxn];
     18   int d[maxn];
     19   int a[maxn];
     20   int p[maxn];
     21 
     22   void init(int n) {
     23     this->n = n;
     24     for (int i = 0; i < n; i++) G[i].clear();
     25     edges.clear();
     26   }
     27 
     28   void add_edge(int from, int to, int cap, int cost) {
     29     edges.push_back((Edge){from, to, cap, 0, cost});
     30     edges.push_back((Edge){to, from, 0, 0, -cost});
     31     m = edges.size();
     32     G[from].push_back(m - 2);
     33     G[to].push_back(m - 1);
     34   }
     35 
     36   bool BF(int s, int t, int flow_limit, int& flow, ll& cost) {
     37     for (int i = 0; i < n; i++)  d[i] = inf;
     38     memset(inq, 0, sizeof inq);
     39     d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = inf;
     40 
     41     queue<int> Q;
     42     Q.push(s);
     43     while (!Q.empty()) {
     44       int u = Q.front(); Q.pop();
     45       inq[u] = 0;
     46       for (int i = 0; i < (int)G[u].size(); i++) {
     47         Edge& e = edges[G[u][i]];
     48         if (e.cap > e.flow && d[e.to] > d[u] + e.cost) {
     49           d[e.to] = d[u] + e.cost;
     50           p[e.to] = G[u][i];
     51           a[e.to] = min(a[u], e.cap - e.flow);
     52           if (!inq[e.to]) {
     53             inq[e.to] = 1;
     54             Q.push(e.to);
     55           }
     56         }
     57       }
     58     }
     59     if (d[t] == inf) return false;
     60     if (flow + a[t] > flow_limit) { a[t] = flow_limit - flow; }
     61     flow += a[t];
     62     cost += 1ll * d[t] * a[t];
     63     for (int u = t; u != s; u = edges[p[u]].from) {
     64       edges[p[u]].flow += a[t];
     65       edges[p[u] ^ 1].flow -= a[t];
     66     }
     67     return true;
     68   }
     69 
     70   int min_cost_max_flow(int s, int t, int flow_limit, ll & cost) {
     71     int flow = 0; cost = 0;
     72     while (flow < flow_limit && BF(s, t, flow_limit, flow, cost));
     73     return flow;
     74   }
     75 };
     76 
     77 MCMF g;
     78 
     79 int main() {
     80   //freopen("case.in", "r", stdin);
     81   int n, e;
     82   while (~scanf("%d%d", &n, &e)) {
     83     g.init(2 * n - 2);
     84     for (int i = 0; i < e; i++) {
     85       int u, v, c;
     86       scanf("%d%d%d", &u, &v, &c);
     87       if (u != 1 && u != n) u += n - 2;
     88       else u--;
     89       v--;
     90       g.add_edge(u, v, 1, c);
     91     }
     92     for (int i = 2; i <= n - 1; i++) {
     93       g.add_edge(i - 1, i + n - 2, 1, 0);
     94     }
     95     ll ans;
     96     g.min_cost_max_flow(0, n - 1, 2, ans);
     97     cout << ans << endl;
     98   }
     99   return 0;
    100 }
    代码君

     

    例题11-10 uva 1349

    题意:给你一个有向图,让你找若干个圈,要求每个点都仅有一个圈包住,并且这些圈的权值之和最小,如果无解就输出N。

    题解:这里用到了一种比较抽象的思考方法,不能单纯地想怎么找这个圈,我们想既然一个点在特定的一个圈内,并且是个有向图,那么必然是只有一个后继,然后每个点都只有一个后继,如果每个点都找得到这样的只有一个后继,那么就说明存在解,反之就是不存在。这个模型对应的就是二分图模型,左边有n个点,右边有n个点,左边到右边只有一个值,所以只需要求一次最小完美匹配即可。注意要处理重边。

    这里我用了KM算法和费用流来编写:

     

     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxn = 1e2 + 10;
     6 const int inf = 1 << 29;
     7 int W[maxn][maxn], n;
     8 int Lx[maxn], Ly[maxn];
     9 int from[maxn];
    10 bool S[maxn], T[maxn];
    11 
    12 bool isMatch(int i) {
    13   S[i] = true;
    14   for (int j = 1; j <= n; j++) if (Lx[i] + Ly[j] == W[i][j] && !T[j]) {
    15     T[j] = true;
    16     if (!from[j] || isMatch(from[j])) {
    17       from[j] = i;
    18       return true;
    19     }
    20   }
    21   return false;
    22 }
    23 
    24 void update() {
    25   int a = 1 << 30;
    26   for (int i = 1; i <= n; i++) if (S[i])
    27     for (int j = 1; j <= n; j++)  if (!T[j])
    28       a =  min(a, Lx[i] + Ly[j] - W[i][j]);
    29   for (int i = 1; i <= n; i++) {
    30     if (S[i]) Lx[i] -= a;
    31     if (T[i]) Ly[i] += a;
    32   }
    33 }
    34 
    35 void KM(int& ret) {
    36   for (int i = 1; i <= n; i++) {
    37     from[i] = Lx[i] = Ly[i] = 0;
    38     for (int j = 1; j <= n; j++)
    39       Lx[i] = max(Lx[i], W[i][j]);
    40   }
    41   for (int i = 1; i <= n; i++) {
    42     for (;;) {
    43       for (int j = 1; j <= n; j++) S[j] = T[j] = 0;
    44       if (isMatch(i)) break;
    45       else update();
    46     }
    47   }
    48   for (int i = 1; i <= n; i++) {
    49     ret += W[from[i]][i];
    50   }
    51 }
    52 
    53 int main() {
    54   //freopen("case.in", "r", stdin);
    55   while (scanf("%d", &n) == 1 && n) {
    56     for (int i = 1; i <= n; i++)
    57       for (int j = 1; j <= n; j++)
    58         W[i][j] = -inf;
    59     for (int u = 1; u <= n; u++) {
    60       int v, w;
    61       while (scanf("%d", &v) && v) {
    62         scanf("%d", &w); w = -w;
    63         W[u][v] = max(W[u][v], w);
    64       }
    65     }
    66     int ans = 0;
    67     KM(ans);
    68     if (ans <= -inf)  printf("N
    ");
    69     else printf("%d
    ", -ans);
    70   }
    71   return 0;
    72 }
    代码君(KM)
     1 /*zhen hao*/
     2 #include <bits/stdc++.h>
     3 using namespace std;
     4 
     5 const int maxn = 1e3 + 10;
     6 const int inf = 1e9;
     7 
     8 struct Edge {
     9   int from, to, cap, flow, cost;
    10 };
    11 
    12 struct MCMF {
    13   int n, m;
    14   vector<Edge> edges;
    15   vector<int> G[maxn];
    16   int inq[maxn];
    17   int d[maxn];
    18   int a[maxn];
    19   int p[maxn];
    20 
    21   void init(int n) {
    22     this->n = n;
    23     for (int i = 0; i < n; i++) G[i].clear();
    24     edges.clear();
    25   }
    26 
    27   void add_edge(int from, int to, int cap, int cost) {
    28     edges.push_back((Edge){from, to, cap, 0, cost});
    29     edges.push_back((Edge){to, from, 0, 0, -cost});
    30     m = edges.size();
    31     G[from].push_back(m - 2);
    32     G[to].push_back(m - 1);
    33   }
    34 
    35   bool BF(int s, int t, int& flow, int& cost) {
    36     for (int i = 0; i < n; i++)  d[i] = inf;
    37     memset(inq, 0, sizeof inq);
    38     d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = inf;
    39 
    40     queue<int> Q;
    41     Q.push(s);
    42     while (!Q.empty()) {
    43       int u = Q.front(); Q.pop();
    44       inq[u] = 0;
    45       for (int i = 0; i < (int)G[u].size(); i++) {
    46         Edge& e = edges[G[u][i]];
    47         if (e.cap > e.flow && d[e.to] > d[u] + e.cost) {
    48           d[e.to] = d[u] + e.cost;
    49           p[e.to] = G[u][i];
    50           a[e.to] = min(a[u], e.cap - e.flow);
    51           if (!inq[e.to]) {
    52             inq[e.to] = 1;
    53             Q.push(e.to);
    54           }
    55         }
    56       }
    57     }
    58     if (d[t] == inf) return false;
    59     flow += a[t];
    60     cost += 1ll * d[t] * a[t];
    61     for (int u = t; u != s; u = edges[p[u]].from) {
    62       edges[p[u]].flow += a[t];
    63       edges[p[u] ^ 1].flow -= a[t];
    64     }
    65     return true;
    66   }
    67 
    68   int min_cost_max_flow(int s, int t, int & cost) {
    69     int flow = 0; cost = 0;
    70     while (BF(s, t, flow, cost));
    71     return flow;
    72   }
    73 };
    74 
    75 MCMF g;
    76 
    77 int main() {
    78   //freopen("case.in", "r", stdin);
    79   int n;
    80   while (scanf("%d", &n) == 1 && n) {
    81     g.init(2 * n + 2);
    82     for (int i = 1; i <= n; i++) {
    83       int v, w;
    84       while (scanf("%d", &v) && v) {
    85         scanf("%d", &w);
    86         g.add_edge(i, v + n, 1, w);
    87       }
    88     }
    89     for (int i = 1; i <= n; i++) {
    90       g.add_edge(0, i, 1, 0);
    91       g.add_edge(n + i, 2 * n + 1, 1, 0);
    92     }
    93     int ans;
    94     if (g.min_cost_max_flow(0, 2 * n + 1, ans) == n) printf("%d
    ", ans);
    95     else printf("N
    ");
    96   }
    97   return 0;
    98 }
    代码君(费用流)
  • 相关阅读:
    servlet中getWriter和getOutputStream的区别
    一个页面访问错误的问题
    sendRedirect实现原理分析
    servlet开发细节
    tomcat 目录分析
    servlet杂谈
    SQL 查询中的like子句的另一种实现方法,速度比like快
    让复合控件的子控件获得设计时支持
    bug管理工具——Gemini
    HtmlAgilityPack获取#开头节点的XPath
  • 原文地址:https://www.cnblogs.com/zhenhao1/p/5548123.html
Copyright © 2020-2023  润新知