• 《算法》第六章部分程序 part 6


    ▶ 书中第六章部分程序,包括在加上自己补充的代码,包括二分图最大匹配(最小顶点覆盖)的交替路径算法和 HopcroftKarp 算法

    ● 二分图最大匹配(最小顶点覆盖)的交替路径算法

      1 package package01;
      2 
      3 import edu.princeton.cs.algs4.StdOut;
      4 import edu.princeton.cs.algs4.BipartiteX;
      5 import edu.princeton.cs.algs4.Graph;
      6 import edu.princeton.cs.algs4.GraphGenerator;
      7 import edu.princeton.cs.algs4.Queue;
      8 
      9 public class class01
     10 {
     11     private final int V;
     12     private BipartiteX bipartition;
     13     private int cardinality;             // 匹配集的顶点数
     14     private int[] mate;                  // 每个顶点的配对顶点,-1 表示未配对
     15     private boolean[] inMinVertexCover;  // 顶点是否处于最小覆盖中
     16     private boolean[] marked;            // 标记已经搜索过的顶点
     17     private int[] edgeTo;                // 搜索序列中的父节点标号
     18 
     19     public class01(Graph G)
     20     {
     21         V = G.V();
     22         mate = new int[V];
     23         for (int v = 0; v < V; v++)
     24             mate[v] = -1;
     25         for (; hasAugmentingPath(G);)
     26         {
     27             int t = -1;
     28             for (int v = 0; v < G.V(); v++)                 // 寻找增广路径的一个端点,它不在 mate 表中,但在搜索序列中
     29             {
     30                 if (!isMatched(v) && edgeTo[v] != -1)
     31                 {
     32                     t = v;
     33                     break;
     34                 }
     35             }
     36             for (int v = t; v != -1; v = edgeTo[edgeTo[v]]) // 增广路经中每两个相邻顶点做成匹配
     37             {
     38                 int w = edgeTo[v];
     39                 mate[v] = w;
     40                 mate[w] = v;
     41             }
     42             cardinality++;                                  // 全部调整完成,匹配集中顶点数量增加 1
     43         }
     44         inMinVertexCover = new boolean[V];                  // 更新 inMinVertexCover[],加入最小覆盖的条件是未匹配的黑点和已匹配的红点
     45         for (int v = 0; v < V; v++)
     46         {
     47             if (bipartition.color(v) && !marked[v] || !bipartition.color(v) && marked[v])
     48                 inMinVertexCover[v] = true;
     49         }
     50         assert certifySolution(G);
     51     }
     52 
     53     private boolean hasAugmentingPath(Graph G)              // 是否存在可调整路径,算法是寻找图中最短增广路经,注意函数对 mate[] 只读
     54                                                             // 交替路径:沿着交替路径前进,每条边依次属于、不属于匹配集合
     55                                                             // 增广路经:起点和终点都属于未匹配集的交替路径,说明可以通过对换路径上所有边(匹配 <-> 未匹配)来增加匹配集的顶点数
     56     {
     57         marked = new boolean[V];                            
     58         edgeTo = new int[V];
     59         for (int v = 0; v < V; v++)
     60             edgeTo[v] = -1;
     61         Queue<Integer> queue = new Queue<Integer>();
     62         for (int v = 0; v < V; v++)
     63         {
     64             if (bipartition.color(v) && !isMatched(v))      // 所有未配对的黑色点(true)加入搜索队列,作为初始点
     65             {
     66                 queue.enqueue(v);
     67                 marked[v] = true;
     68             }
     69         }
     70         for (; !queue.isEmpty();)
     71         {
     72             int v = queue.dequeue();
     73             for (int w : G.adj(v))
     74             {
     75                 if (isResidualGraphEdge(v, w) && !marked[w])// w 是一个新顶点,v - w 是未配对黑-红边且或是已配对红-黑边
     76                 {
     77                     edgeTo[w] = v;                          // 搜索序列中,把 w 当做 v 的后继
     78                     marked[w] = true;
     79                     if (!isMatched(w))                      // w 是未经配对的顶点,说明找到了增广路经,否则 w 也加入搜索队列
     80                         return true;
     81                     queue.enqueue(w);                       
     82                 }
     83             }
     84         }
     85         return false;
     86     }
     87 
     88     private boolean isResidualGraphEdge(int v, int w)       // 要么 v 黑且 v - w 未配对,要么 v 红且 v - w 已配对
     89     {
     90         return (mate[v] != w) && bipartition.color(v) || (mate[v] == w) && !bipartition.color(v);
     91     }
     92 
     93     public int mate(int v)
     94     {
     95         return mate[v];
     96     }
     97 
     98     public boolean isMatched(int v)             // mate[v] >= 0 返回 1,mate[v] == -1 返回 0
     99     {
    100         return mate[v] != -1;
    101     }
    102 
    103     public int size()
    104     {
    105         return cardinality;
    106     }
    107 
    108     public boolean isPerfect()                  // 是否为完美匹配
    109     {
    110         return cardinality * 2 == V;
    111     }
    112 
    113     public boolean inMinVertexCover(int v)      // 顶点是否在最小覆盖中
    114     {
    115         return inMinVertexCover[v];
    116     }
    117 
    118     private boolean certifySolution(Graph G)    // 检查结果正确性
    119     {
    120         for (int v = 0; v < V; v++)             // 检查 mate[] 对称性
    121         {
    122             if (mate(v) == -1)
    123                 continue;
    124             if (mate(mate(v)) != v)
    125                 return false;
    126         }
    127         int matchedVertices = 0;                // 检查 cardinality 与 mate[] 一致性
    128         for (int v = 0; v < V; v++)
    129         {
    130             if (mate(v) != -1)
    131                 matchedVertices++;
    132         }
    133         if (2 * size() != matchedVertices)
    134             return false;
    135         int sizeOfMinVertexCover = 0;           // 检查 cardinality 与 inMinVertexCover[] 一致性
    136         for (int v = 0; v < V; v++)
    137         {
    138             if (inMinVertexCover(v))
    139                 sizeOfMinVertexCover++;
    140         }
    141         if (size() != sizeOfMinVertexCover)
    142             return false;
    143         boolean[] isMatched = new boolean[V];   // 检查 mate[] 唯一性
    144         for (int v = 0; v < V; v++)
    145         {
    146             int w = mate[v];
    147             if (w == -1)
    148                 continue;
    149             if (v == w)
    150                 return false;
    151             if (v >= w)
    152                 continue;
    153             if (isMatched[v] || isMatched[w]) return false;
    154             isMatched[v] = true;
    155             isMatched[w] = true;
    156         }
    157         for (int v = 0; v < V; v++)             // 检查匹配集中的每个顶点至少有一条远端也属于匹配集的边
    158         {
    159             if (mate(v) == -1)
    160                 continue;
    161             boolean isEdge = false;
    162             for (int w : G.adj(v))
    163             {
    164                 if (mate(v) == w)
    165                     isEdge = true;
    166             }
    167             if (!isEdge)
    168                 return false;
    169         }
    170         for (int v = 0; v < V; v++)             // 检查 inMinVertexCover[] 是一个覆盖
    171         {
    172             for (int w : G.adj(v))
    173             {
    174                 if (!inMinVertexCover(v) && !inMinVertexCover(w))
    175                     return false;
    176             }
    177         }
    178         return true;
    179     }
    180 
    181     public static void main(String[] args)
    182     {
    183         int V1 = Integer.parseInt(args[0]);
    184         int V2 = Integer.parseInt(args[1]);
    185         int E = Integer.parseInt(args[2]);
    186         Graph G = GraphGenerator.bipartite(V1, V2, E);
    187         if (G.V() < 1000)
    188             StdOut.println(G);
    189 
    190         class01 matching = new class01(G);
    191 
    192         StdOut.printf("Number of edges in max matching        = %d
    ", matching.size());
    193         StdOut.printf("Number of vertices in min vertex cover = %d
    ", matching.size());
    194         StdOut.printf("Graph has a perfect matching           = %b
    ", matching.isPerfect());
    195         StdOut.println();
    196 
    197         if (G.V() >= 1000)
    198             return;
    199         StdOut.print("Max matching: ");
    200         for (int v = 0; v < G.V(); v++)
    201         {
    202             int w = matching.mate(v);
    203             if (matching.isMatched(v) && v < w)
    204                 StdOut.print(v + "-" + w + " ");
    205         }
    206         StdOut.print("
    Min vertex cover: ");
    207         for (int v = 0; v < G.V(); v++)
    208         {
    209             if (matching.inMinVertexCover(v))
    210                 StdOut.print(v + " ");
    211         }
    212         StdOut.println();
    213     }
    214 }

    ● 二分图最大匹配(最小顶点覆盖)的 HopcroftKarp 算法,仅注释与普通交替路径法不同的地方

      1 package package01;
      2 
      3 import java.util.Iterator;
      4 import edu.princeton.cs.algs4.StdOut;
      5 import edu.princeton.cs.algs4.BipartiteX;
      6 import edu.princeton.cs.algs4.Stack;
      7 import edu.princeton.cs.algs4.Graph;
      8 import edu.princeton.cs.algs4.GraphGenerator;
      9 import edu.princeton.cs.algs4.Queue;
     10 
     11 public class class01
     12 {
     13     private final int V;
     14     private BipartiteX bipartition;
     15     private int cardinality;
     16     private int[] mate;
     17     private boolean[] inMinVertexCover;
     18     private boolean[] marked;
     19     private int[] distTo;                                   // 搜索序列中抵达每个顶点的最小路径长度
     20 
     21     public class01(Graph G)
     22     {
     23         V = G.V();
     24         mate = new int[V];
     25         for (int v = 0; v < V; v++)
     26             mate[v] = -1;
     27         for (; hasAugmentingPath(G);)
     28         {
     29             Iterator<Integer>[] adj = (Iterator<Integer>[]) new Iterator[G.V()];    // 邻接表迭代器列表
     30             for (int v = 0; v < G.V(); v++)
     31                 adj[v] = G.adj(v).iterator();
     32             for (int s = 0; s < V; s++)
     33             {
     34                 if (isMatched(s) || !bipartition.color(s))  // 只取未配对的黑色(true)顶点
     35                     continue;
     36                 Stack<Integer> path = new Stack<Integer>(); // 使用非递归的深度优先遍历寻找关于顶点 s 的交替路径
     37                 for (path.push(s); !path.isEmpty();)
     38                 {
     39                     int v = path.peek();
     40                     if (!adj[v].hasNext())                  // 栈顶顶点没有出边,跳过
     41                     {
     42                         path.pop();
     43                         continue;
     44                     }
     45                     int w = adj[v].next();
     46                     if (!isLevelGraphEdge(v, w))            // 若 v -w 不是搜索队列生成的边,跳过
     47                         continue;
     48                     path.push(w);
     49                     if (!isMatched(w))                      // w 是新点,它不在 mate 表中,但在搜索序列中
     50                     {
     51                         for (; !path.isEmpty();)            // 增广路经中每两个相邻顶点做成匹配
     52                         {
     53                             int x = path.pop(), y = path.pop();
     54                             mate[x] = y;
     55                             mate[y] = x;
     56                         }
     57                         cardinality++;
     58                     }
     59                 }
     60             }
     61         }
     62         inMinVertexCover = new boolean[V];
     63         for (int v = 0; v < V; v++)
     64         {
     65             if (bipartition.color(v) && !marked[v] || !bipartition.color(v) && marked[v])
     66                 inMinVertexCover[v] = true;
     67         }
     68     }
     69 
     70     private boolean hasAugmentingPath(Graph G)
     71     {
     72         marked = new boolean[V];
     73         distTo = new int[V];
     74         for (int v = 0; v < V; v++)
     75             distTo[v] = Integer.MAX_VALUE;
     76         Queue<Integer> queue = new Queue<Integer>();        // 从未配对的顶点开始广度优先搜索
     77         for (int v = 0; v < V; v++)
     78         {
     79             if (bipartition.color(v) && !isMatched(v))      // 所有未配对的黑色点(true)加入搜索队列,作为初始点,初始点距离为 0
     80             {
     81                 queue.enqueue(v);
     82                 marked[v] = true;
     83                 distTo[v] = 0;
     84             }
     85         }
     86         boolean hasAugmentingPath = false;
     87         for (; !queue.isEmpty();)                           // 要运行直到所有顶点都被遍历过才结束
     88         {
     89             int v = queue.dequeue();
     90             for (int w : G.adj(v))
     91             {
     92                 if (isResidualGraphEdge(v, w) && !marked[w])
     93                 {
     94                     distTo[w] = distTo[v] + 1;              // 标记顶点距离为已知顶点加 1 而不标记父节点编号
     95                     marked[w] = true;
     96                     if (!isMatched(w))
     97                         hasAugmentingPath = true;           // 仅标记发现了交替路径而不返回
     98                     if (!hasAugmentingPath)
     99                         queue.enqueue(w);
    100                 }
    101             }
    102         }
    103         return hasAugmentingPath;
    104     }
    105 
    106     private boolean isResidualGraphEdge(int v, int w)
    107     {
    108         return (mate[v] != w) && bipartition.color(v) || (mate[v] == w) && !bipartition.color(v);
    109     }
    110 
    111     private boolean isLevelGraphEdge(int v, int w)          // v - w 是搜索队列生成的边
    112     {
    113         return (distTo[w] == distTo[v] + 1) && isResidualGraphEdge(v, w);
    114     }
    115 
    116     public int mate(int v)
    117     {
    118         return mate[v];
    119     }
    120 
    121     public boolean isMatched(int v)
    122     {
    123         return mate[v] != -1;
    124     }
    125 
    126     public int size()
    127     {
    128         return cardinality;
    129     }
    130 
    131     public boolean isPerfect()
    132     {
    133         return cardinality * 2 == V;
    134     }
    135 
    136     public boolean inMinVertexCover(int v)
    137     {
    138         return inMinVertexCover[v];
    139     }
    140 
    141     private static String toString(Iterable<Integer> path)
    142     {
    143         StringBuilder sb = new StringBuilder();
    144         for (int v : path)
    145             sb.append(v + "-");
    146         String s = sb.toString();
    147         s = s.substring(0, s.lastIndexOf('-'));
    148         return s;
    149     }
    150 
    151     public static void main(String[] args)
    152     {
    153         int V1 = Integer.parseInt(args[0]);
    154         int V2 = Integer.parseInt(args[1]);
    155         int E = Integer.parseInt(args[2]);
    156         Graph G = GraphGenerator.bipartite(V1, V2, E);
    157         if (G.V() < 1000)
    158             StdOut.println(G);
    159 
    160         class01 matching = new class01(G);
    161 
    162         StdOut.printf("Number of edges in max matching        = %d
    ", matching.size());
    163         StdOut.printf("Number of vertices in min vertex cover = %d
    ", matching.size());
    164         StdOut.printf("Graph has a perfect matching           = %b
    ", matching.isPerfect());
    165         StdOut.println();
    166 
    167         if (G.V() >= 1000)
    168             return;
    169         StdOut.print("Max matching: ");
    170         for (int v = 0; v < G.V(); v++)
    171         {
    172             int w = matching.mate(v);
    173             if (matching.isMatched(v) && v < w)
    174                 StdOut.print(v + "-" + w + " ");
    175         }
    176         StdOut.print("
    Min vertex cover: ");
    177         for (int v = 0; v < G.V(); v++)
    178         {
    179             if (matching.inMinVertexCover(v))
    180                 StdOut.print(v + " ");
    181         }
    182         StdOut.println();
    183     }
    184 }
  • 相关阅读:
    日历(Calendar)模块
    关于Python3中函数:
    正则表达式全集
    python同时遍历两个list
    Python 类
    vs_code 快捷键
    Linux常用命令大全
    Linux基础命令sort
    Linux基础命令练习题答案7.10
    Linux基础命令练习题7.10
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/10066440.html
Copyright © 2020-2023  润新知