diff --git a/chapter11.tex b/chapter11.tex index 9c1ba42..227d4dd 100644 --- a/chapter11.tex +++ b/chapter11.tex @@ -22,8 +22,7 @@ and study different ways to represent graphs in algorithms. \index{edge} A \key{graph} consists of \key{nodes} -and \key{edges} between them. -In this book, +and \key{edges}. In this book, the variable $n$ denotes the number of nodes in a graph, and the variable $m$ denotes the number of edges. @@ -57,7 +56,7 @@ through edges of the graph. The \key{length} of a path is the number of edges in it. For example, the above graph contains -the path $1 \rightarrow 3 \rightarrow 4 \rightarrow 5$ +a path $1 \rightarrow 3 \rightarrow 4 \rightarrow 5$ from node 1 to node 5: \begin{center} @@ -87,7 +86,7 @@ from node 1 to node 5: A path is a \key{cycle} if the first and last node is the same. For example, the above graph contains -the cycle $1 \rightarrow 3 \rightarrow 4 \rightarrow 1$. +a cycle $1 \rightarrow 3 \rightarrow 4 \rightarrow 1$. A path is \key{simple} if each node appears at most once in the path. @@ -106,7 +105,7 @@ at most once in the path. \index{connected graph} -A graph is \key{connected} if there is path +A graph is \key{connected} if there is a path between any two nodes. For example, the following graph is connected: \begin{center} @@ -175,7 +174,7 @@ $\{8\}$. A \key{tree} is a connected graph that consists of $n$ nodes and $n-1$ edges. There is a unique path -between any two nodes in a tree. +between any two nodes of a tree. For example, the following graph is a tree: \begin{center} @@ -220,7 +219,7 @@ For example, the following graph is directed: \end{center} The above graph contains -the path $3 \rightarrow 1 \rightarrow 2 \rightarrow 5$ +a path $3 \rightarrow 1 \rightarrow 2 \rightarrow 5$ from node $3$ to node $5$, but there is no path from node $5$ to node $3$. @@ -230,7 +229,7 @@ but there is no path from node $5$ to node $3$. In a \key{weighted} graph, each edge is assigned a \key{weight}. -Often the weights are interpreted as edge lengths. +The weights are often interpreted as edge lengths. For example, the following graph is weighted: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -249,10 +248,10 @@ For example, the following graph is weighted: \end{center} The length of a path in a weighted graph -is the sum of edge weights on the path. +is the sum of the edge weights on the path. For example, in the above graph, the length of the path -$1 \rightarrow 2 \rightarrow 5$ is $12$ +$1 \rightarrow 2 \rightarrow 5$ is $12$, and the length of the path $1 \rightarrow 3 \rightarrow 4 \rightarrow 5$ is $11$. The latter path is the \key{shortest} path from node $1$ to node $5$. @@ -313,7 +312,7 @@ that end at the node, and the \key{outdegree} of a node is the number of edges that start at the node. For example, in the following graph, -the indegree of node 2 is 2 +the indegree of node 2 is 2, and the outdegree of node 2 is 1. \begin{center} @@ -548,7 +547,7 @@ adj[3].push_back({4,5}); adj[4].push_back({1,2}); \end{lstlisting} -The benefit in using adjacency lists is that +The benefit of using adjacency lists is that we can efficiently find the nodes to which we can move from a given node through an edge. For example, the following loop goes through all nodes @@ -677,7 +676,7 @@ corresponds to the following matrix: \end{center} \end{samepage} -The drawback in the adjacency matrix representation +The drawback of the adjacency matrix representation is that there are $n^2$ elements in the matrix and usually most of them are zero. For this reason, the representation cannot be used @@ -753,7 +752,7 @@ For example, the graph \begin{samepage} can be represented as follows\footnote{In some older compilers, the function \texttt{make\_tuple} must be used instead of the braces (for example, -\texttt{make\_tuple(1,2,5)} instead of \texttt{\{1,2,5\}}.}: +\texttt{make\_tuple(1,2,5)} instead of \texttt{\{1,2,5\}}).}: \begin{lstlisting} edges.push_back({1,2,5}); edges.push_back({2,3,7}); diff --git a/chapter12.tex b/chapter12.tex index ddaf3de..2e371a9 100644 --- a/chapter12.tex +++ b/chapter12.tex @@ -19,7 +19,7 @@ is a straightforward graph traversal technique. The algorithm begins at a starting node, and proceeds to all other nodes that are reachable from the starting node using -the edges in the graph. +the edges of the graph. Depth-first search always follows a single path in the graph as long as it finds @@ -48,8 +48,8 @@ the following graph: \path[draw,thick,-] (2) -- (5); \end{tikzpicture} \end{center} -We may begin the search at any node in the graph, -but we will now begin the search at node 1. +We may begin the search at any node of the graph; +now we will begin the search at node 1. The search first proceeds to node 2: \begin{center} @@ -91,7 +91,7 @@ After this, nodes 3 and 5 will be visited: \end{center} The neighbors of node 5 are 2 and 3, but the search has already visited both of them, -so it is time to return to previous nodes. +so it is time to return to the previous nodes. Also the neighbors of nodes 3 and 2 have been visited, so we next move from node 1 to node 4: @@ -174,7 +174,7 @@ have been visited. \subsubsection*{Example} -Let us consider how the algorithm processes +Let us consider how breadth-first search processes the following graph: \begin{center} @@ -186,7 +186,6 @@ the following graph: \node[draw, circle] (5) at (3,3) {$5$}; \node[draw, circle] (6) at (5,3) {$6$}; - \path[draw,thick,-] (1) -- (2); \path[draw,thick,-] (2) -- (3); \path[draw,thick,-] (1) -- (4); @@ -195,7 +194,7 @@ the following graph: \path[draw,thick,-] (5) -- (6); \end{tikzpicture} \end{center} -Suppose again that the search begins at node 1. +Suppose that the search begins at node 1. First, we process all nodes that can be reached from node 1 using a single edge: \begin{center} @@ -276,7 +275,7 @@ Finally, we visit node 6: \end{tikzpicture} \end{center} Now we have calculated the distances -from the starting node to all nodes in the graph. +from the starting node to all nodes of the graph. The distances are as follows: \begin{tabular}{ll} @@ -294,7 +293,7 @@ node & distance \\ Like in depth-first search, the time complexity of breadth-first search -is $O(n+m)$ where $n$ is the number of nodes +is $O(n+m)$, where $n$ is the number of nodes and $m$ is the number of edges. \subsubsection*{Implementation} @@ -318,15 +317,15 @@ int dist[N]; \end{lstlisting} The queue \texttt{q} -contains the nodes in increasing order -of their distance. +contains nodes to be processed +in increasing order of their distance. New nodes are always added to the end of the queue, and the node at the beginning of the queue is the next node to be processed. The array \texttt{vis} indicates which nodes the search has already visited, and the array \texttt{dist} will contain the -distances to all nodes in the graph. +distances from the starting node to all nodes of the graph. The search can be implemented as follows, starting at node $x$: @@ -349,9 +348,9 @@ while (!q.empty()) { \section{Applications} Using the graph traversal algorithms, -we can check many properties of the graph. -Usually, either depth-first search or -bredth-first search can be used, +we can check many properties of graphs. +Usually, both depth-first search and +bredth-first search may be used, but in practice, depth-first search is a better choice, because it is easier to implement. @@ -363,9 +362,9 @@ assume that the graph is undirected. \index{connected graph} A graph is connected if there is a path -between any two nodes in the graph. +between any two nodes of the graph. Thus, we can check if a graph is connected -by choosing an arbitrary node and +by starting at an arbitrary node and finding out if we can reach all other nodes. For example, in the graph @@ -459,9 +458,8 @@ of them as follows: \end{tikzpicture} \end{center} -When we move from -node 2 to node 5 it turns out -that the neighbor 3 has already been visited. +After moving from node 2 to node 5 we notice that +the neighbor 3 of node 5 has already been visited. Thus, the graph contains a cycle that goes through node 3, for example, $3 \rightarrow 2 \rightarrow 5 \rightarrow 3$. diff --git a/chapter13.tex b/chapter13.tex index 746f79b..93ad3bd 100644 --- a/chapter13.tex +++ b/chapter13.tex @@ -2,19 +2,19 @@ \index{shortest path} -Finding the shortest path between two nodes +Finding a shortest path between two nodes of a graph is an important problem that has many -applications in practice. -For example, a natural problem in a road network -is to calculate the length of the shortest route +practical applications. +For example, a natural problem related to a road network +is to calculate the shortest possible length of a route between two cities, given the lengths of the roads. In an unweighted graph, the length of a path equals -the number of edges in the path and we can +the number of its edges, and we can simply use breadth-first search to find -the shortest path. -However, in this chapter we concentrate on +a shortest path. +However, in this chapter we focus on weighted graphs where more sophisticated algorithms are needed @@ -26,9 +26,9 @@ for finding shortest paths. The \key{Bellman–Ford algorithm}\footnote{The algorithm is named after R. E. Bellman and L. R. Ford who published it independently -in 1958 and 1956, respectively \cite{bel58,for56a}.} finds the +in 1958 and 1956, respectively \cite{bel58,for56a}.} finds shortest paths from a starting node to all -other nodes in the graph. +nodes of the graph. The algorithm can process all kinds of graphs, provided that the graph does not contain a cycle with negative length. @@ -36,7 +36,7 @@ If the graph contains a negative cycle, the algorithm can detect this. The algorithm keeps track of distances -from the starting node to other nodes. +from the starting node to all nodes of the graph. Initially, the distance to the starting node is 0 and the distance to all other nodes in infinite. The algorithm reduces the distances by finding @@ -68,7 +68,7 @@ works in the following graph: \path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4); \end{tikzpicture} \end{center} -Each node in the graph is assigned a distance. +Each node of the graph is assigned a distance. Initially, the distance to the starting node is 0, and the distance to all other nodes is infinite. @@ -152,10 +152,10 @@ Finally, there is one more change: \end{center} After this, no edge can reduce any distance. -This means that the distances are final +This means that the distances are final, and we have successfully -calculated the shortest distance -from the starting node to all other nodes. +calculated the shortest distances +from the starting node to all nodes of the graph. For example, the shortest distance 3 from node 1 to node 5 corresponds to @@ -190,8 +190,8 @@ the following path: \subsubsection{Implementation} The following implementation of the -Bellman–Ford algorithm finds the shortest distances -from a node $x$ to all other nodes in the graph. +Bellman–Ford algorithm determines the shortest distances +from a node $x$ to all nodes of the graph. The code assumes that the graph is stored as an edge list \texttt{edges} that consists of tuples of the form $(a,b,w)$, @@ -203,8 +203,8 @@ and on each round the algorithm goes through all edges of the graph and tries to reduce the distances. The algorithm constructs an array \texttt{dist} -that will contain the distance from $x$ -to all nodes in the graph. +that will contain the distances from $x$ +to all nodes of the graph. The constant \texttt{INF} denotes an infinite distance. \begin{lstlisting} @@ -236,7 +236,7 @@ can be reduced during a round. \index{negative cycle} -The Bellman–Ford algorithm can be also used to +The Bellman–Ford algorithm can also be used to check if the graph contains a cycle with negative length. For example, the graph @@ -260,8 +260,8 @@ $2 \rightarrow 3 \rightarrow 4 \rightarrow 2$ with length $-4$. If the graph contains a negative cycle, -we can shorten a path that contains the cycle -infinitely many times by repeating the cycle +we can shorten infinitely many times +any path that contains the cycle by repeating the cycle again and again. Thus, the concept of a shortest path is not meaningful in this situation. @@ -333,10 +333,10 @@ original Bellman–Ford algorithm. \key{Dijkstra's algorithm}\footnote{E. W. Dijkstra published the algorithm in 1959 \cite{dij59}; however, his original paper does not mention how to implement the algorithm efficiently.} -finds the shortest -paths from the starting node to all other nodes, +finds shortest +paths from the starting node to all nodes of the graph, like the Bellman–Ford algorithm. -The benefit in Dijsktra's algorithm is that +The benefit of Dijsktra's algorithm is that it is more efficient and can be used for processing large graphs. However, the algorithm requires that there @@ -415,7 +415,8 @@ and reduces the distances using them: \path[draw=red,thick,->,line width=2pt] (4) -- (5); \end{tikzpicture} \end{center} -The edges from node 1 reduced distances to +In this case, +the edges from node 1 reduced the distances of nodes 2, 4 and 5, whose distances are now 5, 9 and 1. The next node to be processed is node 5 with distance 1: @@ -538,7 +539,7 @@ compensates the previous large weight $6$. The following implementation of Dijkstra's algorithm calculates the minimum distances from a node $x$ -to all other nodes. +to other nodes of the graph. The graph is stored as adjacency lists so that \texttt{adj[$a$]} contains a pair $(b,w)$ always when there is an edge from node $a$ to node $b$ @@ -552,13 +553,14 @@ that contains the nodes ordered by their distances. Using a priority queue, the next node to be processed can be retrieved in logarithmic time. -The following implementation uses a priority queue -\texttt{q} that contains pairs of the form $(-d,x)$: -the current distance to node $x$ is $d$. +In the following code, +the priority queue +\texttt{q} contains pairs of the form $(-d,x)$, +meaning that the current distance to node $x$ is $d$. The array $\texttt{dist}$ contains the distance to each node, and the array $\texttt{ready}$ indicates whether a node has been processed. -Initially the distance to $0$ to $x$ and $\infty$ to all other nodes. +Initially the distance is $0$ to $x$ and $\infty$ to all other nodes. \begin{lstlisting} for (int i = 1; i <= n; i++) dist[i] = INF; @@ -580,8 +582,9 @@ while (!q.empty()) { Note that the priority queue contains \emph{negative} distances to nodes. -The reason for this is that the C++ priority queue finds maximum -elements by default while we want to find minimum elements. +The reason for this is that the +default version of the C++ priority queue finds maximum +elements, while we want to find minimum elements. By using negative distances, we can directly use the default version of the C++ priority queue\footnote{Of course, we could also declare the priority queue as in Chapter 4.5 @@ -591,8 +594,8 @@ node in the priority queue; however, only the instance with the minimum distance will be processed. The time complexity of the above implementation is -$O(n+m \log m)$ because the algorithm goes through -all nodes in the graph and adds for each edge +$O(n+m \log m)$, because the algorithm goes through +all nodes of the graph and adds for each edge at most one distance to the priority queue. \section{Floyd–Warshall algorithm} @@ -602,9 +605,9 @@ at most one distance to the priority queue. The \key{Floyd–Warshall algorithm}\footnote{The algorithm is named after R. W. Floyd and S. Warshall who published it independently in 1962 \cite{flo62,war62}.} -is an alternative way to approach the problem +provides an alternative way to approach the problem of finding shortest paths. -Unlike the other algorithms in this chapter, +Unlike the other algorithms of this chapter, it finds all shortest paths between the nodes in a single run. @@ -775,9 +778,7 @@ for (int i = 1; i <= n; i++) { } } \end{lstlisting} - After this, the shortest distances can be found as follows: - \begin{lstlisting} for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { diff --git a/chapter14.tex b/chapter14.tex index a6f4218..e80ad33 100644 --- a/chapter14.tex +++ b/chapter14.tex @@ -79,7 +79,7 @@ and the parent is node 1. \index{subtree} The structure of a rooted tree is \emph{recursive}: -each node in the tree is the root of a \key{subtree} +each node of the tree acts as the root of a \key{subtree} that contains the node itself and all other nodes that can be reached by traversing down the tree. For example, in the above tree, the subtree of node 4 @@ -88,7 +88,7 @@ consists of nodes 4, 3 and 7. \section{Tree traversal} Depth-first search and breadth-first search -can be used for traversing the nodes in a tree. +can be used for traversing the nodes of a tree. The traversal of a tree is easier to implement than that of a general graph, because there are no cycles in the tree and it is not @@ -129,8 +129,7 @@ to proceed to any direction in the tree. Dynamic programming can be used to calculate some information during a tree traversal. Using dynamic programming, we can, for example, -calculate in $O(n)$ time for each node -in a rooted tree the +calculate in $O(n)$ time for each node of a rooted tree the number of nodes in its subtree or the length of the longest path from the node to a leaf. @@ -158,8 +157,8 @@ void dfs(int s, int e) { \index{diameter} The \key{diameter} of a tree -is the length of the longest path -between two nodes in the tree. +is the maximum length of a path +between two nodes of the tree. For example, in the tree \begin{center} \begin{tikzpicture}[scale=0.9] @@ -194,12 +193,11 @@ to calculate the diameter. First, we root the tree arbitrarily. After this, we use dynamic programming to calculate for each node $x$ -the length of the longest path that begins at some leaf, +the maximum length of a path that begins at some leaf, ascends to $x$ and then descends to another leaf. -The length of the -longest such path equals the diameter of the tree. +The maximum length of such a path equals the diameter of the tree. -In the example graph, the longest path begins at node 7, +In the example graph, a longest path begins at node 7, ascends to node 1, and then descends to node 6: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -225,18 +223,18 @@ ascends to node 1, and then descends to node 6: \end{center} The algorithm first calculates for each node $x$ -the length of the longest path from $x$ to a leaf. +the maximum length of a path from $x$ to a leaf. For example, in the above tree, -the longest path from node 1 to a leaf has length 2 +a longest path from node 1 to a leaf has length 2 (the path can be $1 \rightarrow 4 \rightarrow 3$, $1 \rightarrow 4 \rightarrow 7$ or $1 \rightarrow 2 \rightarrow 6$). After this, the algorithm calculates for each node -$x$ the length of the longest path where $x$ +$x$ the maximum length of a path where $x$ is the highest point of the path. -The longest such path can be found by choosing +Such a path can be found by choosing two children with longest paths to leaves. For example, in the above graph, -nodes 2 and 4 yield the longest path for node 1. +nodes 2 and 4 yield a longest path for node 1. \subsubsection{Algorithm 2} @@ -319,7 +317,7 @@ a starting node of a path that corresponds to the diameter. \section{Distances between nodes} A more difficult problem is to calculate -for each node in the tree and for each direction, +for each node of the tree and for each direction, the maximum distance to a node in that direction. It turns out that this can be calculated in $O(n)$ time using dynamic programming. @@ -498,7 +496,7 @@ For example, the following tree is a binary tree: \index{in-order} \index{post-order} -The nodes in a binary tree have three natural +The nodes of a binary tree have three natural orderings that correspond to different ways to recursively traverse the tree: diff --git a/chapter15.tex b/chapter15.tex index 69ce6d1..37bb3c1 100644 --- a/chapter15.tex +++ b/chapter15.tex @@ -46,7 +46,7 @@ A possible spanning tree for the graph is as follows: \end{tikzpicture} \end{center} -The weight of a spanning tree is the sum of the edge weights. +The weight of a spanning tree is the sum of its edge weights. For example, the weight of the above spanning tree is $3+5+9+3+2=22$. @@ -103,9 +103,8 @@ example graph is 32: \end{tikzpicture} \end{center} -Note that there may be several -minimum and maximum spanning trees -for a graph, +Note that a graph may have several +minimum and maximum spanning trees, so the trees are not unique. This chapter discusses algorithms @@ -166,7 +165,7 @@ following graph: \end{samepage} \begin{samepage} -The first step in the algorithm is to sort the +The first step of the algorithm is to sort the edges in increasing order of their weights. The result is the following list: @@ -211,7 +210,7 @@ Initially, each node is in its own component: \end{tikzpicture} \end{center} The first edge to be added to the tree is -the edge 5--6 that creates the component $\{5,6\}$ +the edge 5--6 that creates a component $\{5,6\}$ by joining the components $\{5\}$ and $\{6\}$: \begin{center} @@ -301,7 +300,7 @@ Why does the greedy strategy guarantee that we will find a minimum spanning tree? Let us see what happens if the minimum weight edge of -the graph is not included in the spanning tree. +the graph is \emph{not} included in the spanning tree. For example, suppose that a spanning tree for the previous graph would not contain the minimum weight edge 5--6. @@ -362,8 +361,8 @@ always produces a minimum spanning tree. \subsubsection{Implementation} When implementing Kruskal's algorithm, -the edge list representation of the graph -is convenient. +it is convenient to use +the edge list representation of the graph. The first phase of the algorithm sorts the edges in the list in $O(m \log m)$ time. After this, the second phase of the algorithm @@ -380,9 +379,9 @@ and always processes an edge $a$--$b$ where $a$ and $b$ are two nodes. Two functions are needed: the function \texttt{same} determines -if the nodes are in the same component, +if $a$ and $b$ are in the same component, and the function \texttt{unite} -joins the components that contain nodes $a$ and $b$. +joins the components that contain $a$ and $b$. The problem is how to efficiently implement the functions \texttt{same} and \texttt{unite}. @@ -446,7 +445,7 @@ $\{1,4,7\}$, $\{5\}$ and $\{2,3,6,8\}$: \end{center} In this case the representatives of the sets are 4, 5 and 2. -For each element, we can find its representative +We can find the representative of any element by following the chain that begins at the element. For example, the element 2 is the representative for the element 6, because @@ -456,7 +455,7 @@ their representatives are the same. Two sets can be joined by connecting the representative of one set to the -representative of another set. +representative of the other set. For example, the sets $\{1,4,7\}$ and $\{2,3,6,8\}$ can be joined as follows: @@ -491,7 +490,7 @@ The efficiency of the union-find structure depends on how the sets are joined. It turns out that we can follow a simple strategy: always connect the representative of the -smaller set to the representative of the larger set +\emph{smaller} set to the representative of the \emph{larger} set (or if the sets are of equal size, we can make an arbitrary choice). Using this strategy, the length of any chain diff --git a/chapter16.tex b/chapter16.tex index 83eaf33..c6e91f3 100644 --- a/chapter16.tex +++ b/chapter16.tex @@ -20,7 +20,7 @@ on the special properties of the graphs. \index{topological sorting} \index{cycle} -A \key{topological sort} is a ordering +A \key{topological sort} is an ordering of the nodes of a directed graph such that if there is a path from node $a$ to node $b$, then node $a$ appears before node $b$ in the ordering. @@ -67,8 +67,8 @@ $[4,1,5,2,3,6]$: An acyclic graph always has a topological sort. However, if the graph contains a cycle, it is not possible to form a topological sort, -because no node in the cycle can appear -before the other nodes in the cycle. +because no node of the cycle can appear +before the other nodes of the cycle in the ordering. It turns out that depth-first search can be used to both check if a directed graph contains a cycle and, if it does not contain a cycle, to construct a topological sort. @@ -92,7 +92,7 @@ its state becomes 1. Finally, after all successors of the node have been processed, its state becomes 2. -If the graph contains a cycle, we will find out this +If the graph contains a cycle, we will find this out during the search, because sooner or later we will arrive at a node whose state is 1. In this case, it is not possible to construct a topological sort. @@ -200,7 +200,7 @@ $[4,5,1,2,3,6]$: \end{center} Note that a topological sort is not unique, -but there can be several topological sorts for a graph. +and there can be several topological sorts for a graph. \subsubsection{Example 2} @@ -248,7 +248,8 @@ The search proceeds as follows: \end{center} The search reaches node 2 whose state is 1, which means the graph contains a cycle. -In this example, the cycle is $2 \rightarrow 3 \rightarrow 5 \rightarrow 2$. +In this example, there is a cycle +$2 \rightarrow 3 \rightarrow 5 \rightarrow 2$. \section{Dynamic programming} @@ -268,7 +269,7 @@ to an ending node: \subsubsection{Counting the number of paths} As an example, let us calculate the number of paths -from node 4 to node 6 in the following graph: +from node 1 to node 6 in the following graph: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -281,25 +282,30 @@ from node 4 to node 6 in the following graph: \path[draw,thick,->,>=latex] (1) -- (2); \path[draw,thick,->,>=latex] (2) -- (3); -\path[draw,thick,->,>=latex] (4) -- (1); +\path[draw,thick,->,>=latex] (1) -- (4); \path[draw,thick,->,>=latex] (4) -- (5); \path[draw,thick,->,>=latex] (5) -- (2); \path[draw,thick,->,>=latex] (5) -- (3); \path[draw,thick,->,>=latex] (3) -- (6); \end{tikzpicture} \end{center} - There are a total of three such paths: \begin{itemize} -\item $4 \rightarrow 1 \rightarrow 2 \rightarrow 3 \rightarrow 6$ -\item $4 \rightarrow 5 \rightarrow 2 \rightarrow 3 \rightarrow 6$ -\item $4 \rightarrow 5 \rightarrow 3 \rightarrow 6$ +\item $1 \rightarrow 2 \rightarrow 3 \rightarrow 6$ +\item $1 \rightarrow 4 \rightarrow 5 \rightarrow 2 \rightarrow 3 \rightarrow 6$ +\item $1 \rightarrow 4 \rightarrow 5 \rightarrow 3 \rightarrow 6$ \end{itemize} -To count the paths, -we go through the nodes in a topological sort, -and calculate for each node $x$ the number of paths -from node 4 to node $x$. +Let $p(x)$ denote the number of paths from +node 1 to node $x$. +As a base case, $p(1)=1$. +Then, to calculate other values of $p(x)$, +we may use the recursion +\[p(x) = p(a_1)+p(a_2)+\cdots+p(a_k)\] +where $a_1,a_2,\ldots,a_k$ are the nodes from which there +is an edge to $x$. +Since the graph is acyclic, the values of $p(x)$ +can be calculated in the order of a topological sort. A topological sort for the above graph is as follows: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -319,7 +325,6 @@ A topological sort for the above graph is as follows: \path[draw,thick,->,>=latex] (3) -- (6); \end{tikzpicture} \end{center} - Hence, the numbers of paths are as follows: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -332,7 +337,7 @@ Hence, the numbers of paths are as follows: \path[draw,thick,->,>=latex] (1) -- (2); \path[draw,thick,->,>=latex] (2) -- (3); -\path[draw,thick,->,>=latex] (4) -- (1); +\path[draw,thick,->,>=latex] (1) -- (4); \path[draw,thick,->,>=latex] (4) -- (5); \path[draw,thick,->,>=latex] (5) -- (2); \path[draw,thick,->,>=latex] (5) -- (3); @@ -347,18 +352,18 @@ Hence, the numbers of paths are as follows: \end{tikzpicture} \end{center} -For example, since there are two paths -from node 4 to node 2 and -there is one path from node 4 to node 5, -we can conclude that there are three -paths from node 4 to node 3. +For example, to calculate the value of $p(3)$, +we may use the formula $p(2)+p(5)$, +because there are edges from nodes 2 and 5 +to node 3. +Since $p(2)=2$ and $p(5)=1$, we conclude that $p(3)=3$. \subsubsection{Extending Dijkstra's algorithm} \index{Dijkstra's algorithm} A by-product of Dijkstra's algorithm is a directed, acyclic -graph that indicates for each node in the original graph +graph that indicates for each node of the original graph the possible ways to reach the node using a shortest path from the starting node. Dynamic programming can be applied to that graph. @@ -470,7 +475,7 @@ the graph is as follows: Using this representation, the shortest path from node 0 to node $x$ -corresponds to a solution with minimum number of coins, +corresponds to a solution with the minimum number of coins, and the total number of paths from node 0 to node $x$ equals the total number of solutions. @@ -480,8 +485,9 @@ equals the total number of solutions. \index{functional graph} For the rest of the chapter, -we will concentrate on \key{successor graphs} -where the outdegree of each node is 1, i.e., +we will focus on \key{successor graphs}. +In those graphs, +the outdegree of each node is 1, i.e., exactly one edge starts at each node. A successor graph consists of one or more components, each of which contains @@ -491,11 +497,10 @@ Successor graphs are sometimes called \key{functional graphs}. The reason for this is that any successor graph corresponds to a function $f$ that defines -the edges in the graph. -The parameter for the function is a node in the graph, -and the function gives the successor of the node. +the edges of the graph. +The parameter for the function is a node of the graph, +and the function gives the successor of that node. -\begin{samepage} For example, the function \begin{center} \begin{tabular}{r|rrrrrrrrr} @@ -504,7 +509,6 @@ $x$ & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\ $f(x)$ & 3 & 5 & 7 & 6 & 2 & 2 & 1 & 6 & 3 \\ \end{tabular} \end{center} -\end{samepage} defines the following graph: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -530,8 +534,8 @@ defines the following graph: \end{tikzpicture} \end{center} -Since each node in a successor graph has a -unique successor, we can define a function $f(x,k)$ +Since each node of a successor graph has a +unique successor, we can also define a function $f(x,k)$ that gives the node that we will reach if we begin at node $x$ and walk $k$ steps forward. For example, in the above graph $f(4,6)=2$, @@ -574,7 +578,7 @@ we can use the following recursion: \end{cases} \end{equation*} -Precalculating values $f(x,k)$ takes $O(n \log u)$ time, +Precalculating the values takes $O(n \log u)$ time, because $O(\log u)$ values are calculated for each node. In the above graph, the first values are as follows: @@ -610,7 +614,7 @@ takes $O(\log k)$ time. Consider a successor graph that only contains a path that ends in a cycle. -There are two interesting questions: +We may ask the following questions: if we begin our walk at the starting node, what is the first node in the cycle and how many nodes does the cycle contain? diff --git a/chapter17.tex b/chapter17.tex index ba8b6a4..578f6dd 100644 --- a/chapter17.tex +++ b/chapter17.tex @@ -516,8 +516,8 @@ their values will be determined according to the values in the component, and if they already have values, they remain unchanged. -The process continues until all variables -have been assigned values. +The process continues until each variable +has been assigned a value. The component graph for the formula $L_1$ is as follows: \begin{center} diff --git a/chapter18.tex b/chapter18.tex index 4ab66ea..41b73eb 100644 --- a/chapter18.tex +++ b/chapter18.tex @@ -52,16 +52,15 @@ and $f(5,2)=0$. An easy way to calculate the value of $f(x,k)$ is to perform a sequence of $k$ moves in the tree. However, the time complexity of this method -is $O(n)$, because the tree may contain -a chain of $O(n)$ nodes. +is $O(k)$, which may be slow, because a tree of $n$ +nodes may have a chain of $n$ nodes. -Fortunately, it turns out that -using a technique similar to that +Fortunately, using a technique similar to that used in Chapter 16.3, any value of $f(x,k)$ can be efficiently calculated in $O(\log k)$ time after preprocessing. The idea is to precalculate all values $f(x,k)$ -where $k$ is a power of two. +where $k \le n$ is a power of two. For example, the values for the above tree are as follows: @@ -77,7 +76,7 @@ $\cdots$ \\ \end{center} The preprocessing takes $O(n \log n)$ time, -because each node can have at most $n$ ancestors. +because $O(\log n)$ values are calculated for each node. After this, any value of $f(x,k)$ can be calculated in $O(\log k)$ time by representing $k$ as a sum where each term is a power of two. @@ -185,10 +184,10 @@ Hence, the corresponding tree traversal array is as follows: \subsubsection{Subtree queries} Each subtree of a tree corresponds to a subarray -in the tree traversal array such that -the first element in the subarray is the root node. +of the tree traversal array such that +the first element of the subarray is the root node. For example, the following subarray contains the -nodes in the subtree of node $4$: +nodes of the subtree of node $4$: \begin{center} \begin{tikzpicture}[scale=0.7] \fill[color=lightgray] (4,0) rectangle (8,1); @@ -382,7 +381,7 @@ can be found as follows: To answer the queries efficiently, it suffices to store the values of the -nodes in a binary indexed tree or segment tree. +nodes in a binary indexed or segment tree. After this, we can both update a value and calculate the sum of values in $O(\log n)$ time. @@ -390,9 +389,9 @@ and calculate the sum of values in $O(\log n)$ time. Using a tree traversal array, we can also efficiently calculate sums of values on -paths from the root node to any other -node in the tree. -Let us next consider a problem where our task +paths from the root node to any +node of the tree. +Let us consider a problem where our task is to support the following queries: \begin{itemize} \item change the value of a node @@ -553,7 +552,7 @@ Thus, to support both the operations, we should be able to increase all values in a range and retrieve a single value. This can be done in $O(\log n)$ time -using a binary indexed tree +using a binary indexed or segment tree (see Chapter 9.4). \section{Lowest common ancestor} @@ -561,11 +560,11 @@ or segment tree (see Chapter 9.4). \index{lowest common ancestor} The \key{lowest common ancestor} -of two nodes in the tree is the lowest node +of two nodes of a rooted tree is the lowest node whose subtree contains both the nodes. A typical problem is to efficiently process queries that ask to find the lowest -common ancestor of given two nodes. +common ancestor of two nodes. For example, in the following tree, the lowest common ancestor of nodes 5 and 8 @@ -605,13 +604,13 @@ Using this, we can divide the problem of finding the lowest common ancestor into two parts. We use two pointers that initially point to the -two nodes for which we should find the -lowest common ancestor. +two nodes whose lowest common ancestor we should find. First, we move one of the pointers upwards -so that both nodes are at the same level in the tree. +so that both pointers point to nodes at the same level. -In the example case, we move from node 8 to node 6, -after which both nodes are at the same level: +In the example case, we move the second pointer one +level up so that it points to node 6 +which is at the same level with node 5: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -638,7 +637,8 @@ after which both nodes are at the same level: After this, we determine the minimum number of steps needed to move both pointers upwards so that they will point to the same node. -This node is the lowest common ancestor of the nodes. +The node to which the pointers point after this +is the lowest common ancestor. In the example case, it suffices to move both pointers one step upwards to node 2, @@ -670,12 +670,12 @@ which is the lowest common ancestor: Since both parts of the algorithm can be performed in $O(\log n)$ time using precomputed information, we can find the lowest common ancestor of any two -nodes in $O(\log n)$ time using this technique. +nodes in $O(\log n)$ time. \subsubsection{Method 2} Another way to solve the problem is based on -a tree traversal array\footnote{This lowest common ancestor algorithm is based on \cite{ben00}. +a tree traversal array\footnote{This lowest common ancestor algorithm was presented in \cite{ben00}. This technique is sometimes called the \index{Euler tour technique} \key{Euler tour technique} \cite{tar84}.}. Once again, the idea is to traverse the nodes @@ -716,7 +716,7 @@ using a depth-first search: \end{tikzpicture} \end{center} -However, we use a bit different tree +However, we use a different tree traversal array than before: we add each node to the array \emph{always} when the depth-first search walks through the node, @@ -792,8 +792,8 @@ The following array corresponds to the above tree: \end{tikzpicture} \end{center} -Using this array, we can find the lowest common ancestor -of nodes $a$ and $b$ by finding the node with lowest level +Now we can find the lowest common ancestor +of nodes $a$ and $b$ by finding the node with the \emph{lowest} level between nodes $a$ and $b$ in the array. For example, the lowest common ancestor of nodes $5$ and $8$ can be found as follows: @@ -881,8 +881,8 @@ after an $O(n \log n)$ time preprocessing. \subsubsection{Distances of nodes} Finally, let us consider the problem of -finding the distance between -two nodes in the tree, which equals +calculating the distance between +two nodes of a tree, which equals the length of the path between them. It turns out that this problem reduces to finding the lowest common ancestor of the nodes. @@ -892,7 +892,7 @@ After this, the distance between nodes $a$ and $b$ can be calculated using the formula \[d(a)+d(b)-2 \cdot d(c),\] where $c$ is the lowest common ancestor of $a$ and $b$ -and $d(s)$ denotes the distance from the root node +and $d(s)$ denotes the distance from the root to node $s$. For example, in the tree @@ -1149,11 +1149,11 @@ algorithms discussed earlier in this chapter. The algorithm is given as input a set of pairs of nodes, and it determines for each such pair the -lowest common ancestor. +lowest common ancestor of the nodes. The algorithm performs a depth-first tree traversal and maintains disjoint sets of nodes. Initially, each node belongs to a separate set. -For each set, we also maintain the highest node in the +For each set, we also store the highest node in the tree that belongs to the set. When the algorithm visits a node $x$, @@ -1164,11 +1164,11 @@ If $y$ has already been visited, the algorithm reports that the lowest common ancestor of $x$ and $y$ is the highest node in the set of $y$. -Then, after processing the subtree of $x$, -the algorithm combines the sets of $x$ and its parent. +Then, after processing node $x$, +the algorithm joins the sets of $x$ and its parent. -For example, assume that we would like the lowest -common ancestor of node pairs $(5,8)$ +For example, assume that we wish to find the lowest +common ancestors of node pairs $(5,8)$ and $(2,7)$ in the following tree: \begin{center} \begin{tikzpicture}[scale=0.85] diff --git a/chapter19.tex b/chapter19.tex index 29383bb..ca1a7e8 100644 --- a/chapter19.tex +++ b/chapter19.tex @@ -7,16 +7,17 @@ goes through each edge exactly once. \item A \key{Hamiltonian path} is a path that visits each node exactly once. \end{itemize} + While Eulerian and Hamiltonian paths look like similar concepts at first glance, the computational problems related to them are very different. It turns out that there is a simple rule that -determines whether a graph contains an Eulerian path +determines whether a graph contains an Eulerian path, and there is also an efficient algorithm to find such a path if it exists. On the contrary, checking the existence of a Hamiltonian path is a NP-hard -problem and no efficient algorithm is known for solving the problem. +problem, and no efficient algorithm is known for solving the problem. \section{Eulerian paths} @@ -25,7 +26,7 @@ problem and no efficient algorithm is known for solving the problem. An \key{Eulerian path}\footnote{L. Euler studied such paths in 1736 when he solved the famous Königsberg bridge problem. This was the birth of graph theory.} is a path -that goes exactly once through each edge in the graph. +that goes exactly once through each edge of the graph. For example, the graph \begin{center} \begin{tikzpicture}[scale=0.9] @@ -116,7 +117,7 @@ has an Eulerian circuit that starts and ends at node 1: \subsubsection{Existence} The existence of Eulerian paths and circuits -depends on the degrees of the nodes in the graph. +depends on the degrees of the nodes of the graph. First, an undirected graph has an Eulerian path if all the edges belong to the same connected component and \begin{itemize} @@ -125,7 +126,7 @@ belong to the same connected component and and the degree of all other nodes is even. \end{itemize} -In the first case, each Eulerian path in the graph +In the first case, each Eulerian path of the graph is also an Eulerian circuit. In the second case, the odd-degree nodes are the starting and ending nodes of an Eulerian path which is not an Eulerian circuit. @@ -165,10 +166,10 @@ connected component and \item in each node, the indegree equals the outdegree, \emph{or} \item in one node, the indegree is one larger than the outdegree, in another node, the outdegree is one larger than the indegree, -and all other nodes, the indegree equals the outdegree. +and in all other nodes, the indegree equals the outdegree. \end{itemize} -In the first case, each Eulerian path in the graph +In the first case, each Eulerian path of the graph is also an Eulerian circuit, and in the second case, the graph contains an Eulerian path that begins at the node whose outdegree is larger @@ -235,7 +236,7 @@ an Eulerian circuit; otherwise Hierholzer's algorithm cannot find it. First, the algorithm constructs a circuit that contains -some (not necessarily all) of the edges in the graph. +some (not necessarily all) of the edges of the graph. After this, the algorithm extends the circuit step by step by adding subcircuits to it. The process continues until all edges have been added @@ -260,7 +261,7 @@ we add the extra edge between the two odd-degree nodes. Next we will see how Hierholzer's algorithm -constructs an Eulerian circuit in an undirected graph. +constructs an Eulerian circuit for an undirected graph. \subsubsection{Example} @@ -401,7 +402,7 @@ so we have successfully constructed an Eulerian circuit. A \key{Hamiltonian path} %\footnote{W. R. Hamilton (1805--1865) was an Irish mathematician.} is a path -that visits each node in the graph exactly once. +that visits each node of the graph exactly once. For example, the graph \begin{center} \begin{tikzpicture}[scale=0.9] @@ -474,9 +475,9 @@ that begins and ends at node 1: \subsubsection{Existence} No efficient method is known for testing if a graph -contains a Hamiltonian path, but the problem is NP-hard. -Still, in some special cases we can be certain -that the graph contains a Hamiltonian path. +contains a Hamiltonian path, and the problem is NP-hard. +Still, in some special cases, we can be certain +that a graph contains a Hamiltonian path. A simple observation is that if the graph is complete, i.e., there is an edge between all pairs of nodes, @@ -507,7 +508,7 @@ the more possibilities there is to construct a Hamiltonian path. Since there is no efficient way to check if a Hamiltonian path exists, it is clear that there is also no method -for constructing the path efficiently, because otherwise +to efficiently construct the path, because otherwise we could just try to construct the path and see whether it exists. @@ -523,7 +524,7 @@ The idea is to define a function $f(s,x)$, where $s$ is a subset of nodes and $x$ is one of the nodes in the subset. The function indicates whether there is a Hamiltonian path -that visits the nodes in $s$ and ends at node $x$. +that visits the nodes of $s$ and ends at node $x$. It is possible to implement this solution in $O(2^n n^2)$ time. \section{De Bruijn sequences} @@ -547,8 +548,8 @@ combinations of three bits: It turns out that each De Bruijn sequence corresponds to an Eulerian path in a graph. -The idea is to construct the graph so that -each node contains a combination of $n-1$ characters +The idea is to construct the graph where +each node contains a string of $n-1$ characters and each edge adds one character to the string. The following graph corresponds to the above example: @@ -575,7 +576,7 @@ The following graph corresponds to the above example: An Eulerian path in this graph corresponds to a string that contains all strings of length $n$. The string contains the characters of the starting node -and all characters in the edges. +and all characters of the edges. The starting node has $n-1$ characters and there are $k^n$ characters in the edges, so the length of the string is $k^n+n-1$. @@ -588,9 +589,9 @@ A \key{knight's tour} is a sequence of moves of a knight on an $n \times n$ chessboard following the rules of chess such that the knight visits each square exactly once. -A tour is \emph{closed} if the knight finally -returns to the starting square and -otherwise it is \emph{open}. +A knight's tour is called a \emph{closed} tour +if the knight finally returns to the starting square and +otherwise it is called an \emph{open} tour. For example, here is an open knight's tour on a $5 \times 5$ board: @@ -626,13 +627,13 @@ For example, here is an open knight's tour on a $5 \times 5$ board: \end{center} A knight's tour corresponds to a Hamiltonian path in a graph -whose nodes represent the squares of the board +whose nodes represent the squares of the board, and two nodes are connected with an edge if a knight can move between the squares according to the rules of chess. A natural way to construct a knight's tour is to use backtracking. The search can be made more efficient by using -\key{heuristics} that attempt to guide the knight so that +\emph{heuristics} that attempt to guide the knight so that a complete tour will be found quickly. \subsubsection{Warnsdorf's rule} @@ -651,7 +652,7 @@ The idea is to always move the knight so that it ends up in a square where the number of possible moves is as \emph{small} as possible. -For example, in the following situation there are five +For example, in the following situation, there are five possible squares to which the knight can move: \begin{center} \begin{tikzpicture}[scale=0.7] diff --git a/chapter20.tex b/chapter20.tex index a4a089a..08d2255 100644 --- a/chapter20.tex +++ b/chapter20.tex @@ -1,6 +1,6 @@ \chapter{Flows and cuts} -In this chapter, we will focus on the following +In this chapter, we focus on the following two problems: \begin{itemize} @@ -14,8 +14,8 @@ that separates two nodes of the graph? The input for both these problems is a directed, weighted graph that contains two special nodes: -the \key{source} is a node with no incoming edges, -and the \key{sink} is a node with no outgoing edges. +the \emph{source} is a node with no incoming edges, +and the \emph{sink} is a node with no outgoing edges. As an example, we will use the following graph where node 1 is the source and node 6 @@ -55,7 +55,7 @@ In each intermediate node, the incoming and outgoing flow has to be equal. -For example, the size of the maximum flow +For example, the maximum size of a flow in the example graph is 7. The following picture shows how we can route the flow: @@ -102,7 +102,7 @@ to the sink after the removal and the total weight of the removed edges is minimum. -The size of the minimum cut in the example graph is 7. +The minimum size of a cut in the example graph is 7. It suffices to remove the edges $2 \rightarrow 3$ and $4 \rightarrow 5$: @@ -140,11 +140,12 @@ way to remove edges from the graph such that their total weight would be less than $7$. \\\\ It is not a coincidence that -both the size of the maximum flow and -the size of the minimum cut is 7 in the above example. -It turns out that the size of the maximum flow -and the minimum cut is -\emph{always} the same, +the maximum size of a flow +and the minimum size of a cut +are the same in the above example. +It turns out that a maximum flow +and a minimum cut are +\emph{always} equally large, so the concepts are two sides of the same coin. Next we will discuss the Ford–Fulkerson @@ -160,10 +161,10 @@ The algorithm also helps us to understand The \key{Ford–Fulkerson algorithm} \cite{for56} finds the maximum flow in a graph. The algorithm begins with an empty flow, -and at each step finds a path in the graph -that generates more flow. +and at each step finds a path from the source +to the sink that generates more flow. Finally, when the algorithm cannot increase the flow -anymore, it terminates and the maximum flow has been found. +anymore, the maximum flow has been found. The algorithm uses a special representation of the graph where each original edge has a reverse @@ -293,7 +294,7 @@ and the new graph is as follows: The idea is that increasing the flow decreases the amount of flow that can go through the edges in the future. -On the other hand, it is possible to modify the +On the other hand, it is possible to cancel flow later using the reverse edges of the graph if it turns out that it would be beneficial to route the flow in another way. @@ -435,7 +436,7 @@ chooses each path so that the number of edges on the path is as small as possible. This can be done by using breadth-first search instead of depth-first search for finding paths. -It turns out that this guarantees that +It can be proven that this guarantees that the flow increases quickly, and the time complexity of the algorithm is $O(m^2 n)$. @@ -445,8 +446,8 @@ The \key{scaling algorithm} \cite{ahu91} uses depth-first search to find paths where each edge weight is at least a threshold value. Initially, the threshold value is -the sum of capacities of the edges that -start at the source. +some large number, for example the sum of all +edge weights of the graph. Always when a path cannot be found, the threshold value is divided by 2. The time complexity of the algorithm is $O(m^2 \log c)$, @@ -463,7 +464,7 @@ that typically appear in programming contests. It turns out that once the Ford–Fulkerson algorithm has found a maximum flow, -it has also found a minimum cut. +it has also determined a minimum cut. Let $A$ be the set of nodes that can be reached from the source using positive-weight edges. @@ -509,14 +510,14 @@ Why is the flow produced by the algorithm maximum and why is the cut minimum? The reason is that a graph cannot contain a flow whose size is larger -than the weight of any cut in the graph. +than the weight of any cut of the graph. Hence, always when a flow and a cut are equally large, they are a maximum flow and a minimum cut. -Let us consider any cut in the graph +Let us consider any cut of the graph such that the source belongs to $A$, the sink belongs to $B$ -and there are edges between the sets: +and there are some edges between the sets: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -645,8 +646,8 @@ to the sink. In this problem, every node, except for the source and sink, may appear in at most one path. -The number of node-disjoint paths is -often smaller than the number of +The number of node-disjoint paths +may be smaller than the number of edge-disjoint paths. For example, in the previous graph, @@ -682,9 +683,9 @@ Since each node can appear in at most one path, we have to limit the flow that goes through the nodes. A standard method for this is to divide each node into two nodes such that the first node has the incoming edges -of the original node and the second node has the outgoing -edges of the original node. -In addition, there is a new edge from the first node +of the original node, the second node has the outgoing +edges of the original node, and +there is a new edge from the first node to the second node. In our example, the graph becomes as follows: @@ -772,7 +773,7 @@ from the source to the sink is 1. \index{maximum matching} The \key{maximum matching} problem asks to find -a maximum-size set of node pairs in a graph +a maximum-size set of node pairs of a graph such that each pair is connected with an edge and each node belongs to at most one pair. @@ -787,10 +788,11 @@ maximum flow problem. \subsubsection{Finding maximum matchings} -The nodes in a bipartite graph can be always +The nodes of a bipartite graph can be always divided into two groups such that all edges of the graph go from the left group to the right group. -For example, consider the following bipartite graph: +For example, in the following bipartite graph, +the groups are $\{1,2,3,4\}$ and $\{5,6,7,8\}$. \begin{center} \begin{tikzpicture}[scale=0.60] @@ -811,8 +813,7 @@ For example, consider the following bipartite graph: \path[draw,thick,-] (4) -- (7); \end{tikzpicture} \end{center} - -In this graph, the size of a maximum matching is 3: +The size of a maximum matching of the graph is 3: \begin{center} \begin{tikzpicture}[scale=0.60] \node[draw, circle] (1) at (2,4.5) {1}; @@ -840,7 +841,7 @@ In this graph, the size of a maximum matching is 3: We can reduce the bipartite maximum matching problem to the maximum flow problem by adding two new nodes to the graph: a source and a sink. -In addition, we add edges from the source +We also add edges from the source to each left node and from each right node to the sink. After this, the maximum flow of the graph equals the maximum matching of the original graph. @@ -996,7 +997,7 @@ Next, let $X=\{2,4\}$ which yields $f(X)=\{7\}$: In this case, $|X|=2$ and $|f(X)|=1$, so the condition of Hall's theorem does not hold. This means that it is not possible to form -a perfect matching in the graph. +a perfect matching for the graph. This result is not surprising, because we already know that the maximum matching of the graph is 3 and not 4. @@ -1115,7 +1116,7 @@ set is as follows: \index{path cover} -A \key{path cover} is a set of paths in a graph +A \key{path cover} is a set of paths of a graph such that each node of the graph belongs to at least one path. It turns out that in directed, acyclic graphs, we can reduce the problem of finding a minimum @@ -1172,19 +1173,20 @@ Note that one of the paths only contains node 2, so it is possible that a path does not contain any edges. We can find a minimum node-disjoint path cover -by constructing a matching graph where each node +by constructing a \emph{matching graph} where each node of the original graph is represented by two nodes: a left node and a right node. There is an edge from a left node to a right node if there is a such an edge in the original graph. -In addition, the matching graph contains a source and a sink -such that there are edges from the source to all +In addition, the matching graph contains a source and a sink, +and there are edges from the source to all left nodes and from all right nodes to the sink. -A maximum matching in the resulting graph corresponds +A maximum matching of the resulting graph corresponds to a minimum node-disjoint path cover in the original graph. -For example, the following graph contains +For example, the following matching graph +for the above graph contains a maximum matching of size 4: \begin{center} @@ -1252,7 +1254,7 @@ A \key{general path cover} is a path cover where a node can belong to more than one path. A minimum general path cover may be smaller than a minimum node-disjoint path cover, -because a node can used multiple times in paths. +because a node can be used multiple times in paths. Consider again the following graph: \begin{center} \begin{tikzpicture}[scale=0.9] @@ -1273,7 +1275,7 @@ Consider again the following graph: \end{tikzpicture} \end{center} -The minimum general path cover in this graph +The minimum general path cover of this graph consists of two paths. For example, the first path may be as follows: \begin{center}