graph day version
This commit is contained in:
parent
f269ae3919
commit
55a61a0050
59
book.tex
59
book.tex
|
@ -53,12 +53,12 @@
|
||||||
stringstyle=\color{strings}
|
stringstyle=\color{strings}
|
||||||
}
|
}
|
||||||
|
|
||||||
\date{Draft \today}
|
\date{modified by Johannes Kapfhammer, February 2021}
|
||||||
|
|
||||||
\usepackage[a4paper,vmargin=30mm,hmargin=33mm,footskip=15mm]{geometry}
|
\usepackage[a4paper,vmargin=30mm,hmargin=33mm,footskip=15mm]{geometry}
|
||||||
|
|
||||||
\title{\Huge Competitive Programmer's Handbook}
|
\title{\Huge SOI Camp 2021 -- Graph Day}
|
||||||
\author{\Large Antti Laaksonen}
|
\author{\Large Competitive Programmer’s Handbook by Antti Laaksonen}
|
||||||
|
|
||||||
\makeindex
|
\makeindex
|
||||||
\usepackage[totoc]{idxlayout}
|
\usepackage[totoc]{idxlayout}
|
||||||
|
@ -86,39 +86,42 @@
|
||||||
|
|
||||||
\newcommand{\key}[1] {\textbf{#1}}
|
\newcommand{\key}[1] {\textbf{#1}}
|
||||||
|
|
||||||
\part{Basic techniques}
|
%\part{Basic techniques}
|
||||||
\include{chapter01}
|
%\include{chapter01}
|
||||||
\include{chapter02}
|
%\include{chapter02}
|
||||||
\include{chapter03}
|
%\include{chapter03}
|
||||||
\include{chapter04}
|
%\include{chapter04}
|
||||||
\include{chapter05}
|
%\include{chapter05}
|
||||||
\include{chapter06}
|
%\include{chapter06}
|
||||||
\include{chapter07}
|
%\include{chapter07}
|
||||||
\include{chapter08}
|
%\include{chapter08}
|
||||||
\include{chapter09}
|
%\include{chapter09}
|
||||||
\include{chapter10}
|
%\include{chapter10}
|
||||||
\part{Graph algorithms}
|
\part{Main Topics}
|
||||||
\include{chapter11}
|
\include{chapter11}
|
||||||
\include{chapter12}
|
\include{chapter12}
|
||||||
\include{chapter13}
|
\include{chapter13}
|
||||||
\include{chapter14}
|
\include{chapter14}
|
||||||
\include{chapter15}
|
|
||||||
\include{chapter16}
|
\include{chapter16}
|
||||||
|
%\include{chapter20}
|
||||||
|
|
||||||
|
\part{Advanced topics}
|
||||||
|
\include{chapter15}
|
||||||
\include{chapter17}
|
\include{chapter17}
|
||||||
\include{chapter18}
|
\include{chapter18}
|
||||||
\include{chapter19}
|
\include{chapter19}
|
||||||
\include{chapter20}
|
|
||||||
\part{Advanced topics}
|
% \part{Advanced topics}
|
||||||
\include{chapter21}
|
%\include{chapter21}
|
||||||
\include{chapter22}
|
%\include{chapter22}
|
||||||
\include{chapter23}
|
%\include{chapter23}
|
||||||
\include{chapter24}
|
%\include{chapter24}
|
||||||
\include{chapter25}
|
%\include{chapter25}
|
||||||
\include{chapter26}
|
%\include{chapter26}
|
||||||
\include{chapter27}
|
%\include{chapter27}
|
||||||
\include{chapter28}
|
%\include{chapter28}
|
||||||
\include{chapter29}
|
%\include{chapter29}
|
||||||
\include{chapter30}
|
%\include{chapter30}
|
||||||
|
|
||||||
\cleardoublepage
|
\cleardoublepage
|
||||||
\phantomsection
|
\phantomsection
|
||||||
|
|
|
@ -478,21 +478,19 @@ way to represent graphs, and most algorithms can be
|
||||||
efficiently implemented using them.
|
efficiently implemented using them.
|
||||||
|
|
||||||
A convenient way to store the adjacency lists is to declare
|
A convenient way to store the adjacency lists is to declare
|
||||||
an array of vectors as follows:
|
a vector of vectors as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
vector<int> adj[N];
|
vector<vector<int>> g;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
The constant $N$ is chosen so that all
|
|
||||||
adjacency lists can be stored.
|
|
||||||
For example, the graph
|
For example, the graph
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
\node[draw, circle] (1) at (1,3) {$1$};
|
\node[draw, circle] (1) at (1,3) {$0$};
|
||||||
\node[draw, circle] (2) at (3,3) {$2$};
|
\node[draw, circle] (2) at (3,3) {$1$};
|
||||||
\node[draw, circle] (3) at (5,3) {$3$};
|
\node[draw, circle] (3) at (5,3) {$2$};
|
||||||
\node[draw, circle] (4) at (3,1) {$4$};
|
\node[draw, circle] (4) at (3,1) {$3$};
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- (2);
|
\path[draw,thick,->,>=latex] (1) -- (2);
|
||||||
\path[draw,thick,->,>=latex] (2) -- (3);
|
\path[draw,thick,->,>=latex] (2) -- (3);
|
||||||
|
@ -503,11 +501,12 @@ For example, the graph
|
||||||
\end{center}
|
\end{center}
|
||||||
can be stored as follows:
|
can be stored as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
adj[1].push_back(2);
|
g.assign(4, {}); // g now consists of 4 empty arrays
|
||||||
adj[2].push_back(3);
|
g[0].push_back(1);
|
||||||
adj[2].push_back(4);
|
g[1].push_back(2);
|
||||||
adj[3].push_back(4);
|
g[1].push_back(3);
|
||||||
adj[4].push_back(1);
|
g[2].push_back(3);
|
||||||
|
g[3].push_back(0);
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
If the graph is undirected, it can be stored in a similar way,
|
If the graph is undirected, it can be stored in a similar way,
|
||||||
|
@ -517,7 +516,7 @@ For a weighted graph, the structure can be extended
|
||||||
as follows:
|
as follows:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
vector<pair<int,int>> adj[N];
|
vector<vector<pair<int,int>>> g;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
In this case, the adjacency list of node $a$
|
In this case, the adjacency list of node $a$
|
||||||
|
@ -527,10 +526,10 @@ with weight $w$. For example, the graph
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
\node[draw, circle] (1) at (1,3) {$1$};
|
\node[draw, circle] (1) at (1,3) {$0$};
|
||||||
\node[draw, circle] (2) at (3,3) {$2$};
|
\node[draw, circle] (2) at (3,3) {$1$};
|
||||||
\node[draw, circle] (3) at (5,3) {$3$};
|
\node[draw, circle] (3) at (5,3) {$2$};
|
||||||
\node[draw, circle] (4) at (3,1) {$4$};
|
\node[draw, circle] (4) at (3,1) {$3$};
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- node[font=\small,label=above:5] {} (2);
|
\path[draw,thick,->,>=latex] (1) -- node[font=\small,label=above:5] {} (2);
|
||||||
\path[draw,thick,->,>=latex] (2) -- node[font=\small,label=above:7] {} (3);
|
\path[draw,thick,->,>=latex] (2) -- node[font=\small,label=above:7] {} (3);
|
||||||
|
@ -541,11 +540,12 @@ with weight $w$. For example, the graph
|
||||||
\end{center}
|
\end{center}
|
||||||
can be stored as follows:
|
can be stored as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
adj[1].push_back({2,5});
|
g.assign(4, {});
|
||||||
adj[2].push_back({3,7});
|
g[0].push_back({1,5});
|
||||||
adj[2].push_back({4,6});
|
g[1].push_back({2,7});
|
||||||
adj[3].push_back({4,5});
|
g[1].push_back({3,6});
|
||||||
adj[4].push_back({1,2});
|
g[2].push_back({3,5});
|
||||||
|
g[3].push_back({0,2});
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
The benefit of using adjacency lists is that
|
The benefit of using adjacency lists is that
|
||||||
|
@ -555,7 +555,7 @@ For example, the following loop goes through all nodes
|
||||||
to which we can move from node $s$:
|
to which we can move from node $s$:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (auto u : adj[s]) {
|
for (auto u : g[s]) {
|
||||||
// process node u
|
// process node u
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
@ -570,7 +570,8 @@ We can efficiently check from an adjacency matrix
|
||||||
if there is an edge between two nodes.
|
if there is an edge between two nodes.
|
||||||
The matrix can be stored as an array
|
The matrix can be stored as an array
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int adj[N][N];
|
vector<vector<int>> adj;
|
||||||
|
adj.assign(n, vector<int>(n, 0));
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
where each value $\texttt{adj}[a][b]$ indicates
|
where each value $\texttt{adj}[a][b]$ indicates
|
||||||
whether the graph contains an edge from
|
whether the graph contains an edge from
|
||||||
|
@ -581,10 +582,10 @@ and otherwise $\texttt{adj}[a][b]=0$.
|
||||||
For example, the graph
|
For example, the graph
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
\node[draw, circle] (1) at (1,3) {$1$};
|
\node[draw, circle] (1) at (1,3) {$0$};
|
||||||
\node[draw, circle] (2) at (3,3) {$2$};
|
\node[draw, circle] (2) at (3,3) {$1$};
|
||||||
\node[draw, circle] (3) at (5,3) {$3$};
|
\node[draw, circle] (3) at (5,3) {$2$};
|
||||||
\node[draw, circle] (4) at (3,1) {$4$};
|
\node[draw, circle] (4) at (3,1) {$3$};
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- (2);
|
\path[draw,thick,->,>=latex] (1) -- (2);
|
||||||
\path[draw,thick,->,>=latex] (2) -- (3);
|
\path[draw,thick,->,>=latex] (2) -- (3);
|
||||||
|
@ -704,10 +705,10 @@ Thus, the graph
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
\node[draw, circle] (1) at (1,3) {$1$};
|
\node[draw, circle] (1) at (1,3) {$0$};
|
||||||
\node[draw, circle] (2) at (3,3) {$2$};
|
\node[draw, circle] (2) at (3,3) {$1$};
|
||||||
\node[draw, circle] (3) at (5,3) {$3$};
|
\node[draw, circle] (3) at (5,3) {$2$};
|
||||||
\node[draw, circle] (4) at (3,1) {$4$};
|
\node[draw, circle] (4) at (3,1) {$3$};
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- (2);
|
\path[draw,thick,->,>=latex] (1) -- (2);
|
||||||
\path[draw,thick,->,>=latex] (2) -- (3);
|
\path[draw,thick,->,>=latex] (2) -- (3);
|
||||||
|
@ -718,11 +719,11 @@ Thus, the graph
|
||||||
\end{center}
|
\end{center}
|
||||||
can be represented as follows:
|
can be represented as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
edges.push_back({1,2});
|
edges.push_back({0,2});
|
||||||
edges.push_back({2,3});
|
edges.push_back({1,3});
|
||||||
|
edges.push_back({1,4});
|
||||||
edges.push_back({2,4});
|
edges.push_back({2,4});
|
||||||
edges.push_back({3,4});
|
edges.push_back({3,1});
|
||||||
edges.push_back({4,1});
|
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\noindent
|
\noindent
|
||||||
|
@ -738,10 +739,10 @@ For example, the graph
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}[scale=0.9]
|
\begin{tikzpicture}[scale=0.9]
|
||||||
\node[draw, circle] (1) at (1,3) {$1$};
|
\node[draw, circle] (1) at (1,3) {$0$};
|
||||||
\node[draw, circle] (2) at (3,3) {$2$};
|
\node[draw, circle] (2) at (3,3) {$1$};
|
||||||
\node[draw, circle] (3) at (5,3) {$3$};
|
\node[draw, circle] (3) at (5,3) {$2$};
|
||||||
\node[draw, circle] (4) at (3,1) {$4$};
|
\node[draw, circle] (4) at (3,1) {$3$};
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- node[font=\small,label=above:5] {} (2);
|
\path[draw,thick,->,>=latex] (1) -- node[font=\small,label=above:5] {} (2);
|
||||||
\path[draw,thick,->,>=latex] (2) -- node[font=\small,label=above:7] {} (3);
|
\path[draw,thick,->,>=latex] (2) -- node[font=\small,label=above:7] {} (3);
|
||||||
|
@ -755,10 +756,10 @@ 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} 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}
|
\begin{lstlisting}
|
||||||
edges.push_back({1,2,5});
|
edges.push_back({0,2,5});
|
||||||
edges.push_back({2,3,7});
|
edges.push_back({1,3,7});
|
||||||
edges.push_back({2,4,6});
|
edges.push_back({1,4,6});
|
||||||
edges.push_back({3,4,5});
|
edges.push_back({2,4,5});
|
||||||
edges.push_back({4,1,2});
|
edges.push_back({3,1,2});
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
\end{samepage}
|
\end{samepage}
|
||||||
|
|
|
@ -129,11 +129,11 @@ a depth-first search at a given node.
|
||||||
The function assumes that the graph is
|
The function assumes that the graph is
|
||||||
stored as adjacency lists in an array
|
stored as adjacency lists in an array
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
vector<int> adj[N];
|
vector<vector<int>> g;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
and also maintains an array
|
and also maintains an array
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
bool visited[N];
|
vector<bool> visited;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
that keeps track of the visited nodes.
|
that keeps track of the visited nodes.
|
||||||
Initially, each array value is \texttt{false},
|
Initially, each array value is \texttt{false},
|
||||||
|
@ -145,7 +145,7 @@ void dfs(int s) {
|
||||||
if (visited[s]) return;
|
if (visited[s]) return;
|
||||||
visited[s] = true;
|
visited[s] = true;
|
||||||
// process node s
|
// process node s
|
||||||
for (auto u: adj[s]) {
|
for (auto u : g[s]) {
|
||||||
dfs(u);
|
dfs(u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,8 +312,8 @@ as adjacency lists and maintains the following
|
||||||
data structures:
|
data structures:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
queue<int> q;
|
queue<int> q;
|
||||||
bool visited[N];
|
vector<bool> visited(n);
|
||||||
int distance[N];
|
vector<int> distance(N);
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
The queue \texttt{q}
|
The queue \texttt{q}
|
||||||
|
@ -336,7 +336,7 @@ q.push(x);
|
||||||
while (!q.empty()) {
|
while (!q.empty()) {
|
||||||
int s = q.front(); q.pop();
|
int s = q.front(); q.pop();
|
||||||
// process node s
|
// process node s
|
||||||
for (auto u : adj[s]) {
|
for (auto u : g[s]) {
|
||||||
if (visited[u]) continue;
|
if (visited[u]) continue;
|
||||||
visited[u] = true;
|
visited[u] = true;
|
||||||
distance[u] = distance[s]+1;
|
distance[u] = distance[s]+1;
|
||||||
|
|
578
chapter13.tex
578
chapter13.tex
|
@ -20,313 +20,6 @@ where more sophisticated algorithms
|
||||||
are needed
|
are needed
|
||||||
for finding shortest paths.
|
for finding shortest paths.
|
||||||
|
|
||||||
\section{Bellman–Ford algorithm}
|
|
||||||
|
|
||||||
\index{Bellman–Ford algorithm}
|
|
||||||
|
|
||||||
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
|
|
||||||
shortest paths from a starting node to all
|
|
||||||
nodes of the graph.
|
|
||||||
The algorithm can process all kinds of graphs,
|
|
||||||
provided that the graph does not contain a
|
|
||||||
cycle with negative length.
|
|
||||||
If the graph contains a negative cycle,
|
|
||||||
the algorithm can detect this.
|
|
||||||
|
|
||||||
The algorithm keeps track of distances
|
|
||||||
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
|
|
||||||
edges that shorten the paths until it is not
|
|
||||||
possible to reduce any distance.
|
|
||||||
|
|
||||||
\subsubsection{Example}
|
|
||||||
|
|
||||||
Let us consider how the Bellman–Ford algorithm
|
|
||||||
works in the following graph:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (1,3) {1};
|
|
||||||
\node[draw, circle] (2) at (4,3) {2};
|
|
||||||
\node[draw, circle] (3) at (1,1) {3};
|
|
||||||
\node[draw, circle] (4) at (4,1) {4};
|
|
||||||
\node[draw, circle] (5) at (6,2) {6};
|
|
||||||
\node[color=red] at (1,3+0.55) {$0$};
|
|
||||||
\node[color=red] at (4,3+0.55) {$\infty$};
|
|
||||||
\node[color=red] at (1,1-0.55) {$\infty$};
|
|
||||||
\node[color=red] at (4,1-0.55) {$\infty$};
|
|
||||||
\node[color=red] at (6,2-0.55) {$\infty$};
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:5] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:1] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
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.
|
|
||||||
|
|
||||||
The algorithm searches for edges that reduce distances.
|
|
||||||
First, all edges from node 1 reduce distances:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (1,3) {1};
|
|
||||||
\node[draw, circle] (2) at (4,3) {2};
|
|
||||||
\node[draw, circle] (3) at (1,1) {3};
|
|
||||||
\node[draw, circle] (4) at (4,1) {4};
|
|
||||||
\node[draw, circle] (5) at (6,2) {5};
|
|
||||||
\node[color=red] at (1,3+0.55) {$0$};
|
|
||||||
\node[color=red] at (4,3+0.55) {$5$};
|
|
||||||
\node[color=red] at (1,1-0.55) {$3$};
|
|
||||||
\node[color=red] at (4,1-0.55) {$7$};
|
|
||||||
\node[color=red] at (6,2-0.55) {$\infty$};
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:5] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:1] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
||||||
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (1) -- (3);
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
After this, edges
|
|
||||||
$2 \rightarrow 5$ and $3 \rightarrow 4$
|
|
||||||
reduce distances:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (1,3) {1};
|
|
||||||
\node[draw, circle] (2) at (4,3) {2};
|
|
||||||
\node[draw, circle] (3) at (1,1) {3};
|
|
||||||
\node[draw, circle] (4) at (4,1) {4};
|
|
||||||
\node[draw, circle] (5) at (6,2) {5};
|
|
||||||
\node[color=red] at (1,3+0.55) {$0$};
|
|
||||||
\node[color=red] at (4,3+0.55) {$5$};
|
|
||||||
\node[color=red] at (1,1-0.55) {$3$};
|
|
||||||
\node[color=red] at (4,1-0.55) {$4$};
|
|
||||||
\node[color=red] at (6,2-0.55) {$7$};
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:5] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:1] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
||||||
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (2) -- (5);
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (3) -- (4);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
Finally, there is one more change:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (1,3) {1};
|
|
||||||
\node[draw, circle] (2) at (4,3) {2};
|
|
||||||
\node[draw, circle] (3) at (1,1) {3};
|
|
||||||
\node[draw, circle] (4) at (4,1) {4};
|
|
||||||
\node[draw, circle] (5) at (6,2) {5};
|
|
||||||
\node[color=red] at (1,3+0.55) {$0$};
|
|
||||||
\node[color=red] at (4,3+0.55) {$5$};
|
|
||||||
\node[color=red] at (1,1-0.55) {$3$};
|
|
||||||
\node[color=red] at (4,1-0.55) {$4$};
|
|
||||||
\node[color=red] at (6,2-0.55) {$6$};
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:5] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:1] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
||||||
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
After this, no edge can reduce any distance.
|
|
||||||
This means that the distances are final,
|
|
||||||
and we have successfully
|
|
||||||
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
|
|
||||||
the following path:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (1,3) {1};
|
|
||||||
\node[draw, circle] (2) at (4,3) {2};
|
|
||||||
\node[draw, circle] (3) at (1,1) {3};
|
|
||||||
\node[draw, circle] (4) at (4,1) {4};
|
|
||||||
\node[draw, circle] (5) at (6,2) {5};
|
|
||||||
\node[color=red] at (1,3+0.55) {$0$};
|
|
||||||
\node[color=red] at (4,3+0.55) {$5$};
|
|
||||||
\node[color=red] at (1,1-0.55) {$3$};
|
|
||||||
\node[color=red] at (4,1-0.55) {$4$};
|
|
||||||
\node[color=red] at (6,2-0.55) {$6$};
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:5] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:3] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:1] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=left:3] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:2] {} (5);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (4);
|
|
||||||
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (1) -- (3);
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (3) -- (4);
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
\subsubsection{Implementation}
|
|
||||||
|
|
||||||
The following implementation of the
|
|
||||||
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)$,
|
|
||||||
meaning that there is an edge from node $a$ to node $b$
|
|
||||||
with weight $w$.
|
|
||||||
|
|
||||||
The algorithm consists of $n-1$ rounds,
|
|
||||||
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{distance}
|
|
||||||
that will contain the distances from $x$
|
|
||||||
to all nodes of the graph.
|
|
||||||
The constant \texttt{INF} denotes an infinite distance.
|
|
||||||
|
|
||||||
\begin{lstlisting}
|
|
||||||
for (int i = 1; i <= n; i++) distance[i] = INF;
|
|
||||||
distance[x] = 0;
|
|
||||||
for (int i = 1; i <= n-1; i++) {
|
|
||||||
for (auto e : edges) {
|
|
||||||
int a, b, w;
|
|
||||||
tie(a, b, w) = e;
|
|
||||||
distance[b] = min(distance[b], distance[a]+w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
\end{lstlisting}
|
|
||||||
|
|
||||||
The time complexity of the algorithm is $O(nm)$,
|
|
||||||
because the algorithm consists of $n-1$ rounds and
|
|
||||||
iterates through all $m$ edges during a round.
|
|
||||||
If there are no negative cycles in the graph,
|
|
||||||
all distances are final after $n-1$ rounds,
|
|
||||||
because each shortest path can contain at most $n-1$ edges.
|
|
||||||
|
|
||||||
In practice, the final distances can usually
|
|
||||||
be found faster than in $n-1$ rounds.
|
|
||||||
Thus, a possible way to make the algorithm more efficient
|
|
||||||
is to stop the algorithm if no distance
|
|
||||||
can be reduced during a round.
|
|
||||||
|
|
||||||
\subsubsection{Negative cycles}
|
|
||||||
|
|
||||||
\index{negative cycle}
|
|
||||||
|
|
||||||
The Bellman–Ford algorithm can also be used to
|
|
||||||
check if the graph contains a cycle with negative length.
|
|
||||||
For example, the graph
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (1) at (0,0) {$1$};
|
|
||||||
\node[draw, circle] (2) at (2,1) {$2$};
|
|
||||||
\node[draw, circle] (3) at (2,-1) {$3$};
|
|
||||||
\node[draw, circle] (4) at (4,0) {$4$};
|
|
||||||
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:$3$] {} (2);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:$1$] {} (4);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=below:$5$] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:$-7$] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=right:$2$] {} (3);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
\noindent
|
|
||||||
contains a negative cycle
|
|
||||||
$2 \rightarrow 3 \rightarrow 4 \rightarrow 2$
|
|
||||||
with length $-4$.
|
|
||||||
|
|
||||||
If the graph contains a negative 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.
|
|
||||||
|
|
||||||
A negative cycle can be detected
|
|
||||||
using the Bellman–Ford algorithm by
|
|
||||||
running the algorithm for $n$ rounds.
|
|
||||||
If the last round reduces any distance,
|
|
||||||
the graph contains a negative cycle.
|
|
||||||
Note that this algorithm can be used to
|
|
||||||
search for
|
|
||||||
a negative cycle in the whole graph
|
|
||||||
regardless of the starting node.
|
|
||||||
|
|
||||||
\subsubsection{SPFA algorithm}
|
|
||||||
|
|
||||||
\index{SPFA algorithm}
|
|
||||||
|
|
||||||
The \key{SPFA algorithm} (''Shortest Path Faster Algorithm'') \cite{fan94}
|
|
||||||
is a variant of the Bellman–Ford algorithm,
|
|
||||||
that is often more efficient than the original algorithm.
|
|
||||||
The SPFA algorithm does not go through all the edges on each round,
|
|
||||||
but instead, it chooses the edges to be examined
|
|
||||||
in a more intelligent way.
|
|
||||||
|
|
||||||
The algorithm maintains a queue of nodes that might
|
|
||||||
be used for reducing the distances.
|
|
||||||
First, the algorithm adds the starting node $x$
|
|
||||||
to the queue.
|
|
||||||
Then, the algorithm always processes the
|
|
||||||
first node in the queue, and when an edge
|
|
||||||
$a \rightarrow b$ reduces a distance,
|
|
||||||
node $b$ is added to the queue.
|
|
||||||
%
|
|
||||||
% The following implementation uses a
|
|
||||||
% \texttt{queue} \texttt{q}.
|
|
||||||
% In addition, an array \texttt{inqueue} indicates
|
|
||||||
% if a node is already in the queue,
|
|
||||||
% in which case the algorithm does not add
|
|
||||||
% the node to the queue again.
|
|
||||||
%
|
|
||||||
% \begin{lstlisting}
|
|
||||||
% for (int i = 1; i <= n; i++) distance[i] = INF;
|
|
||||||
% distance[x] = 0;
|
|
||||||
% q.push(x);
|
|
||||||
% while (!q.empty()) {
|
|
||||||
% int a = q.front(); q.pop();
|
|
||||||
% inqueue[a] = false;
|
|
||||||
% for (auto b : v[a]) {
|
|
||||||
% if (distance[a]+b.second < distance[b.first]) {
|
|
||||||
% distance[b.first] = distance[a]+b.second;
|
|
||||||
% if (!inqueue[b]) {q.push(b); inqueue[b] = true;}
|
|
||||||
% }
|
|
||||||
% }
|
|
||||||
% }
|
|
||||||
% \end{lstlisting}
|
|
||||||
|
|
||||||
The efficiency of the SPFA algorithm depends
|
|
||||||
on the structure of the graph:
|
|
||||||
the algorithm is often efficient,
|
|
||||||
but its worst case time complexity is still
|
|
||||||
$O(nm)$ and it is possible to create inputs
|
|
||||||
that make the algorithm as slow as the
|
|
||||||
original Bellman–Ford algorithm.
|
|
||||||
|
|
||||||
\section{Dijkstra's algorithm}
|
\section{Dijkstra's algorithm}
|
||||||
|
|
||||||
\index{Dijkstra's algorithm}
|
\index{Dijkstra's algorithm}
|
||||||
|
@ -334,15 +27,12 @@ original Bellman–Ford algorithm.
|
||||||
\key{Dijkstra's algorithm}\footnote{E. W. Dijkstra published the algorithm in 1959 \cite{dij59};
|
\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.}
|
however, his original paper does not mention how to implement the algorithm efficiently.}
|
||||||
finds shortest
|
finds shortest
|
||||||
paths from the starting node to all nodes of the graph,
|
paths from the starting node to all nodes of the graph.
|
||||||
like the Bellman–Ford algorithm.
|
Dijkstra's algorithm is very efficient and can be used for
|
||||||
The benefit of Dijsktra's algorithm is that
|
|
||||||
it is more efficient and can be used for
|
|
||||||
processing large graphs.
|
processing large graphs.
|
||||||
However, the algorithm requires that there
|
However, the algorithm requires that there
|
||||||
are no negative weight edges in the graph.
|
are no negative weight edges in the graph.
|
||||||
|
|
||||||
Like the Bellman–Ford algorithm,
|
|
||||||
Dijkstra's algorithm maintains distances
|
Dijkstra's algorithm maintains distances
|
||||||
to the nodes and reduces them during the search.
|
to the nodes and reduces them during the search.
|
||||||
Dijkstra's algorithm is efficient, because
|
Dijkstra's algorithm is efficient, because
|
||||||
|
@ -543,9 +233,9 @@ The following implementation of Dijkstra's algorithm
|
||||||
calculates the minimum distances from a node $x$
|
calculates the minimum distances from a node $x$
|
||||||
to other nodes of the graph.
|
to other nodes of the graph.
|
||||||
The graph is stored as adjacency lists
|
The graph is stored as adjacency lists
|
||||||
so that \texttt{adj[$a$]} contains a pair $(b,w)$
|
so that \texttt{g[$v$]} contains a pair $(w,\text{cost})$
|
||||||
always when there is an edge from node $a$ to node $b$
|
always when there is an edge from node $v$ to node $w$
|
||||||
with weight $w$.
|
with weight $\text{cost}$.
|
||||||
|
|
||||||
An efficient implementation of Dijkstra's algorithm
|
An efficient implementation of Dijkstra's algorithm
|
||||||
requires that it is possible to efficiently find the
|
requires that it is possible to efficiently find the
|
||||||
|
@ -556,40 +246,42 @@ Using a priority queue, the next node to be processed
|
||||||
can be retrieved in logarithmic time.
|
can be retrieved in logarithmic time.
|
||||||
|
|
||||||
In the following code, the priority queue
|
In the following code, the priority queue
|
||||||
\texttt{q} contains pairs of the form $(-d,x)$,
|
\texttt{pq} contains pairs of the form $(d,x)$,
|
||||||
meaning that the current distance to node $x$ is $d$.
|
meaning that the current distance to node $x$ is $d$.
|
||||||
|
|
||||||
The array $\texttt{distance}$ contains the distance to
|
The array $\texttt{distance}$ contains the distance to
|
||||||
each node, and the array $\texttt{processed}$ indicates
|
each node. Initially, the distance is $0$ to $\text{start}$ and $-1$ to all
|
||||||
whether a node has been processed.
|
other nodes. We use $-1$ as invalid value to denote that the node
|
||||||
Initially the distance is $0$ to $x$ and $\infty$ to all other nodes.
|
has not been reached yet.
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
for (int i = 1; i <= n; i++) distance[i] = INF;
|
vector<int> distance(n, -1);
|
||||||
distance[x] = 0;
|
priority_queue<pair<int, int>,
|
||||||
q.push({0,x});
|
vector<pair<int, int>>,
|
||||||
while (!q.empty()) {
|
greater<pair<int, int>>> pq;
|
||||||
int a = q.top().second; q.pop();
|
distance[start] = 0;
|
||||||
if (processed[a]) continue;
|
pq.emplace(0, start);
|
||||||
processed[a] = true;
|
while (!pq.empty()) {
|
||||||
for (auto u : adj[a]) {
|
auto [d, v] = q.top();
|
||||||
int b = u.first, w = u.second;
|
q.pop();
|
||||||
if (distance[a]+w < distance[b]) {
|
if (distance[v] != -1) continue;
|
||||||
distance[b] = distance[a]+w;
|
distance[v] = d;
|
||||||
q.push({-distance[b],b});
|
for (auto [w, cost] : g[v])
|
||||||
}
|
q.emplace(d + cost, w);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
Note that the priority queue contains \emph{negative}
|
Note that the type of the priority queue is not
|
||||||
distances to nodes.
|
\verb|priority_queue<pair<int, int>| but instead
|
||||||
The reason for this is that the
|
\verb|priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>|.
|
||||||
default version of the C++ priority queue finds maximum
|
This is because in C++, a priority queue by default puts the
|
||||||
elements, while we want to find minimum elements.
|
\emph{largest} element on top, so we reverse the ordering by changing
|
||||||
By using negative distances,
|
the comparison operator from \verb|less| (the default) to
|
||||||
we can directly use the default priority queue\footnote{Of
|
\verb|greater| (which does the opposite).
|
||||||
course, we could also declare the priority queue as in Chapter 4.5
|
|
||||||
and use positive distances, but the implementation would be a bit longer.}.
|
In case you forget, you can look up the syntax for the priority queue
|
||||||
|
in the C++ cheatsheet linked on the camp page.
|
||||||
|
|
||||||
Also note that there may be several instances of the same
|
Also note that there may be several instances of the same
|
||||||
node in the priority queue; however, only the instance with the
|
node in the priority queue; however, only the instance with the
|
||||||
minimum distance will be processed.
|
minimum distance will be processed.
|
||||||
|
@ -598,205 +290,3 @@ The time complexity of the above implementation is
|
||||||
$O(n+m \log m)$, because the algorithm goes through
|
$O(n+m \log m)$, because the algorithm goes through
|
||||||
all nodes of the graph and adds for each edge
|
all nodes of the graph and adds for each edge
|
||||||
at most one distance to the priority queue.
|
at most one distance to the priority queue.
|
||||||
|
|
||||||
\section{Floyd–Warshall algorithm}
|
|
||||||
|
|
||||||
\index{Floyd–Warshall algorithm}
|
|
||||||
|
|
||||||
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}.}
|
|
||||||
provides an alternative way to approach the problem
|
|
||||||
of finding shortest paths.
|
|
||||||
Unlike the other algorithms of this chapter,
|
|
||||||
it finds all shortest paths between the nodes
|
|
||||||
in a single run.
|
|
||||||
|
|
||||||
The algorithm maintains a two-dimensional array
|
|
||||||
that contains distances between the nodes.
|
|
||||||
First, distances are calculated only using
|
|
||||||
direct edges between the nodes,
|
|
||||||
and after this, the algorithm reduces distances
|
|
||||||
by using intermediate nodes in paths.
|
|
||||||
|
|
||||||
\subsubsection{Example}
|
|
||||||
|
|
||||||
Let us consider how the Floyd–Warshall algorithm
|
|
||||||
works in the following graph:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (1) at (1,3) {$3$};
|
|
||||||
\node[draw, circle] (2) at (4,3) {$4$};
|
|
||||||
\node[draw, circle] (3) at (1,1) {$2$};
|
|
||||||
\node[draw, circle] (4) at (4,1) {$1$};
|
|
||||||
\node[draw, circle] (5) at (6,2) {$5$};
|
|
||||||
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
Initially, the distance from each node to itself is $0$,
|
|
||||||
and the distance between nodes $a$ and $b$ is $x$
|
|
||||||
if there is an edge between nodes $a$ and $b$ with weight $x$.
|
|
||||||
All other distances are infinite.
|
|
||||||
|
|
||||||
In this graph, the initial array is as follows:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tabular}{r|rrrrr}
|
|
||||||
& 1 & 2 & 3 & 4 & 5 \\
|
|
||||||
\hline
|
|
||||||
1 & 0 & 5 & $\infty$ & 9 & 1 \\
|
|
||||||
2 & 5 & 0 & 2 & $\infty$ & $\infty$ \\
|
|
||||||
3 & $\infty$ & 2 & 0 & 7 & $\infty$ \\
|
|
||||||
4 & 9 & $\infty$ & 7 & 0 & 2 \\
|
|
||||||
5 & 1 & $\infty$ & $\infty$ & 2 & 0 \\
|
|
||||||
\end{tabular}
|
|
||||||
\end{center}
|
|
||||||
\vspace{10pt}
|
|
||||||
The algorithm consists of consecutive rounds.
|
|
||||||
On each round, the algorithm selects a new node
|
|
||||||
that can act as an intermediate node in paths from now on,
|
|
||||||
and distances are reduced using this node.
|
|
||||||
|
|
||||||
On the first round, node 1 is the new intermediate node.
|
|
||||||
There is a new path between nodes 2 and 4
|
|
||||||
with length 14, because node 1 connects them.
|
|
||||||
There is also a new path
|
|
||||||
between nodes 2 and 5 with length 6.
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tabular}{r|rrrrr}
|
|
||||||
& 1 & 2 & 3 & 4 & 5 \\
|
|
||||||
\hline
|
|
||||||
1 & 0 & 5 & $\infty$ & 9 & 1 \\
|
|
||||||
2 & 5 & 0 & 2 & \textbf{14} & \textbf{6} \\
|
|
||||||
3 & $\infty$ & 2 & 0 & 7 & $\infty$ \\
|
|
||||||
4 & 9 & \textbf{14} & 7 & 0 & 2 \\
|
|
||||||
5 & 1 & \textbf{6} & $\infty$ & 2 & 0 \\
|
|
||||||
\end{tabular}
|
|
||||||
\end{center}
|
|
||||||
\vspace{10pt}
|
|
||||||
|
|
||||||
On the second round, node 2 is the new intermediate node.
|
|
||||||
This creates new paths between nodes 1 and 3
|
|
||||||
and between nodes 3 and 5:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tabular}{r|rrrrr}
|
|
||||||
& 1 & 2 & 3 & 4 & 5 \\
|
|
||||||
\hline
|
|
||||||
1 & 0 & 5 & \textbf{7} & 9 & 1 \\
|
|
||||||
2 & 5 & 0 & 2 & 14 & 6 \\
|
|
||||||
3 & \textbf{7} & 2 & 0 & 7 & \textbf{8} \\
|
|
||||||
4 & 9 & 14 & 7 & 0 & 2 \\
|
|
||||||
5 & 1 & 6 & \textbf{8} & 2 & 0 \\
|
|
||||||
\end{tabular}
|
|
||||||
\end{center}
|
|
||||||
\vspace{10pt}
|
|
||||||
|
|
||||||
On the third round, node 3 is the new intermediate round.
|
|
||||||
There is a new path between nodes 2 and 4:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tabular}{r|rrrrr}
|
|
||||||
& 1 & 2 & 3 & 4 & 5 \\
|
|
||||||
\hline
|
|
||||||
1 & 0 & 5 & 7 & 9 & 1 \\
|
|
||||||
2 & 5 & 0 & 2 & \textbf{9} & 6 \\
|
|
||||||
3 & 7 & 2 & 0 & 7 & 8 \\
|
|
||||||
4 & 9 & \textbf{9} & 7 & 0 & 2 \\
|
|
||||||
5 & 1 & 6 & 8 & 2 & 0 \\
|
|
||||||
\end{tabular}
|
|
||||||
\end{center}
|
|
||||||
\vspace{10pt}
|
|
||||||
|
|
||||||
The algorithm continues like this,
|
|
||||||
until all nodes have been appointed intermediate nodes.
|
|
||||||
After the algorithm has finished, the array contains
|
|
||||||
the minimum distances between any two nodes:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tabular}{r|rrrrr}
|
|
||||||
& 1 & 2 & 3 & 4 & 5 \\
|
|
||||||
\hline
|
|
||||||
1 & 0 & 5 & 7 & 3 & 1 \\
|
|
||||||
2 & 5 & 0 & 2 & 8 & 6 \\
|
|
||||||
3 & 7 & 2 & 0 & 7 & 8 \\
|
|
||||||
4 & 3 & 8 & 7 & 0 & 2 \\
|
|
||||||
5 & 1 & 6 & 8 & 2 & 0 \\
|
|
||||||
\end{tabular}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
For example, the array tells us that the
|
|
||||||
shortest distance between nodes 2 and 4 is 8.
|
|
||||||
This corresponds to the following path:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (1) at (1,3) {$3$};
|
|
||||||
\node[draw, circle] (2) at (4,3) {$4$};
|
|
||||||
\node[draw, circle] (3) at (1,1) {$2$};
|
|
||||||
\node[draw, circle] (4) at (4,1) {$1$};
|
|
||||||
\node[draw, circle] (5) at (6,2) {$5$};
|
|
||||||
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:7] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:2] {} (3);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:5] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=left:9] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (5);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
||||||
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (3) -- (4);
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (4) -- (5);
|
|
||||||
\path[draw=red,thick,->,line width=2pt] (5) -- (2);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
\subsubsection{Implementation}
|
|
||||||
|
|
||||||
The advantage of the
|
|
||||||
Floyd–Warshall algorithm that it is
|
|
||||||
easy to implement.
|
|
||||||
The following code constructs a
|
|
||||||
distance matrix where $\texttt{distance}[a][b]$
|
|
||||||
is the shortest distance between nodes $a$ and $b$.
|
|
||||||
First, the algorithm initializes \texttt{distance}
|
|
||||||
using the adjacency matrix \texttt{adj} of the graph:
|
|
||||||
|
|
||||||
\begin{lstlisting}
|
|
||||||
for (int i = 1; i <= n; i++) {
|
|
||||||
for (int j = 1; j <= n; j++) {
|
|
||||||
if (i == j) distance[i][j] = 0;
|
|
||||||
else if (adj[i][j]) distance[i][j] = adj[i][j];
|
|
||||||
else distance[i][j] = INF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
\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++) {
|
|
||||||
for (int j = 1; j <= n; j++) {
|
|
||||||
distance[i][j] = min(distance[i][j],
|
|
||||||
distance[i][k]+distance[k][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
\end{lstlisting}
|
|
||||||
|
|
||||||
The time complexity of the algorithm is $O(n^3)$,
|
|
||||||
because it contains three nested loops
|
|
||||||
that go through the nodes of the graph.
|
|
||||||
|
|
||||||
Since the implementation of the Floyd–Warshall
|
|
||||||
algorithm is simple, the algorithm can be
|
|
||||||
a good choice even if it is only needed to find a
|
|
||||||
single shortest path in the graph.
|
|
||||||
However, the algorithm can only be used when the graph
|
|
||||||
is so small that a cubic time complexity is fast enough.
|
|
||||||
|
|
|
@ -113,17 +113,16 @@ a depth-first search at an arbitrary node.
|
||||||
The following recursive function can be used:
|
The following recursive function can be used:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
void dfs(int s, int e) {
|
void dfs(int v, int p) {
|
||||||
// process node s
|
for (auto w : g[v])
|
||||||
for (auto u : adj[s]) {
|
if (w != p)
|
||||||
if (u != e) dfs(u, s);
|
dfs(w, v);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
The function is given two parameters: the current node $s$
|
The function is given two parameters: the current node $v$
|
||||||
and the previous node $e$.
|
and the previous node $p$.
|
||||||
The purpose of the parameter $e$ is to make sure
|
The purpose of the parameter $p$ is to make sure
|
||||||
that the search only moves to nodes
|
that the search only moves to nodes
|
||||||
that have not been visited yet.
|
that have not been visited yet.
|
||||||
|
|
||||||
|
@ -131,37 +130,36 @@ The following function call starts the search
|
||||||
at node $x$:
|
at node $x$:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
dfs(x, 0);
|
dfs(x, -1);
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
In the first call $e=0$, because there is no
|
In the first call $p=-1$, because there is no
|
||||||
previous node, and it is allowed
|
previous node, and it is allowed
|
||||||
to proceed to any direction in the tree.
|
to proceed to any direction in the tree.
|
||||||
|
|
||||||
\subsubsection{Dynamic programming}
|
\subsubsection{Storing Information}
|
||||||
|
We can calculate
|
||||||
Dynamic programming can be used to calculate
|
some information during a tree traversal and store that for later use.
|
||||||
some information during a tree traversal.
|
We can, for example,
|
||||||
Using dynamic programming, we can, for example,
|
|
||||||
calculate in $O(n)$ time for each node of a rooted tree the
|
calculate in $O(n)$ time for each node of a rooted tree the
|
||||||
number of nodes in its subtree
|
number of nodes in its subtree
|
||||||
or the length of the longest path from the node
|
or the length of the longest path from the node
|
||||||
to a leaf.
|
to a leaf.
|
||||||
|
|
||||||
As an example, let us calculate for each node $s$
|
As an example, let us calculate for each node $v$
|
||||||
a value $\texttt{count}[s]$: the number of nodes in its subtree.
|
a value $\texttt{subtreesize}[v]$: the number of nodes in its subtree.
|
||||||
The subtree contains the node itself and
|
The subtree contains the node itself and
|
||||||
all nodes in the subtrees of its children,
|
all nodes in the subtrees of its children,
|
||||||
so we can calculate the number of nodes
|
so we can calculate the number of nodes
|
||||||
recursively using the following code:
|
recursively using the following code:
|
||||||
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
void dfs(int s, int e) {
|
void dfs(int v, int p) {
|
||||||
count[s] = 1;
|
subtreesize[s] = 1;
|
||||||
for (auto u : adj[s]) {
|
for (auto w : g[v]) {
|
||||||
if (u == e) continue;
|
if (w == p) continue;
|
||||||
dfs(u, s);
|
dfs(w, v);
|
||||||
count[s] += count[u];
|
subtreesize[s] += subtreesize[u];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
@ -220,7 +218,7 @@ to obtain another path with length 4.
|
||||||
|
|
||||||
Next we will discuss two $O(n)$ time algorithms
|
Next we will discuss two $O(n)$ time algorithms
|
||||||
for calculating the diameter of a tree.
|
for calculating the diameter of a tree.
|
||||||
The first algorithm is based on dynamic programming,
|
The first algorithm is based on the previous idea of storing information,
|
||||||
and the second algorithm uses two depth-first searches.
|
and the second algorithm uses two depth-first searches.
|
||||||
|
|
||||||
\subsubsection{Algorithm 1}
|
\subsubsection{Algorithm 1}
|
||||||
|
@ -279,7 +277,7 @@ because there is a path
|
||||||
$6 \rightarrow 2 \rightarrow 1 \rightarrow 4 \rightarrow 7$.
|
$6 \rightarrow 2 \rightarrow 1 \rightarrow 4 \rightarrow 7$.
|
||||||
In this case, $\texttt{maxLength}(1)$ equals the diameter.
|
In this case, $\texttt{maxLength}(1)$ equals the diameter.
|
||||||
|
|
||||||
Dynamic programming can be used to calculate the above
|
We can calculate the above
|
||||||
values for all nodes in $O(n)$ time.
|
values for all nodes in $O(n)$ time.
|
||||||
First, to calculate $\texttt{toLeaf}(x)$,
|
First, to calculate $\texttt{toLeaf}(x)$,
|
||||||
we go through the children of $x$,
|
we go through the children of $x$,
|
||||||
|
@ -448,8 +446,8 @@ goes through its child 2:
|
||||||
\path[draw,thick,->,color=red,line width=2pt] (3) -- (6);
|
\path[draw,thick,->,color=red,line width=2pt] (3) -- (6);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
This part is easy to solve in $O(n)$ time, because we can use
|
This part is easy to solve in $O(n)$ time, because we can use a
|
||||||
dynamic programming as we have done previously.
|
similar technique to what we have done previously.
|
||||||
|
|
||||||
Then, the second part of the problem is to calculate
|
Then, the second part of the problem is to calculate
|
||||||
for every node $x$ the maximum length of a path
|
for every node $x$ the maximum length of a path
|
||||||
|
|
475
chapter16.tex
475
chapter16.tex
|
@ -1,20 +1,4 @@
|
||||||
\chapter{Directed graphs}
|
\chapter{Topological sorting}
|
||||||
|
|
||||||
In this chapter, we focus on two classes of directed graphs:
|
|
||||||
\begin{itemize}
|
|
||||||
\item \key{Acyclic graphs}:
|
|
||||||
There are no cycles in the graph,
|
|
||||||
so there is no path from any node to itself\footnote{Directed acyclic
|
|
||||||
graphs are sometimes called DAGs.}.
|
|
||||||
\item \key{Successor graphs}:
|
|
||||||
The outdegree of each node is 1,
|
|
||||||
so each node has a unique successor.
|
|
||||||
\end{itemize}
|
|
||||||
It turns out that in both cases,
|
|
||||||
we can design efficient algorithms that are based
|
|
||||||
on the special properties of the graphs.
|
|
||||||
|
|
||||||
\section{Topological sorting}
|
|
||||||
|
|
||||||
\index{topological sorting}
|
\index{topological sorting}
|
||||||
\index{cycle}
|
\index{cycle}
|
||||||
|
@ -249,460 +233,3 @@ The search reaches node 2 whose state is 1,
|
||||||
which means that the graph contains a cycle.
|
which means that the graph contains a cycle.
|
||||||
In this example, there is a cycle
|
In this example, there is a cycle
|
||||||
$2 \rightarrow 3 \rightarrow 5 \rightarrow 2$.
|
$2 \rightarrow 3 \rightarrow 5 \rightarrow 2$.
|
||||||
|
|
||||||
\section{Dynamic programming}
|
|
||||||
|
|
||||||
If a directed graph is acyclic,
|
|
||||||
dynamic programming can be applied to it.
|
|
||||||
For example, we can efficiently solve the following
|
|
||||||
problems concerning paths from a starting node
|
|
||||||
to an ending node:
|
|
||||||
|
|
||||||
\begin{itemize}
|
|
||||||
\item how many different paths are there?
|
|
||||||
\item what is the shortest/longest path?
|
|
||||||
\item what is the minimum/maximum number of edges in a path?
|
|
||||||
\item which nodes certainly appear in any path?
|
|
||||||
\end{itemize}
|
|
||||||
|
|
||||||
\subsubsection{Counting the number of paths}
|
|
||||||
|
|
||||||
As an example, let us calculate the number of paths
|
|
||||||
from node 1 to node 6 in the following graph:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (1) at (1,5) {$1$};
|
|
||||||
\node[draw, circle] (2) at (3,5) {$2$};
|
|
||||||
\node[draw, circle] (3) at (5,5) {$3$};
|
|
||||||
\node[draw, circle] (4) at (1,3) {$4$};
|
|
||||||
\node[draw, circle] (5) at (3,3) {$5$};
|
|
||||||
\node[draw, circle] (6) at (5,3) {$6$};
|
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- (2);
|
|
||||||
\path[draw,thick,->,>=latex] (2) -- (3);
|
|
||||||
\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 $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}
|
|
||||||
|
|
||||||
Let $\texttt{paths}(x)$ denote the number of paths from
|
|
||||||
node 1 to node $x$.
|
|
||||||
As a base case, $\texttt{paths}(1)=1$.
|
|
||||||
Then, to calculate other values of $\texttt{paths}(x)$,
|
|
||||||
we may use the recursion
|
|
||||||
\[\texttt{paths}(x) = \texttt{paths}(a_1)+\texttt{paths}(a_2)+\cdots+\texttt{paths}(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 $\texttt{paths}(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]
|
|
||||||
\node[draw, circle] (1) at (0,0) {$1$};
|
|
||||||
\node[draw, circle] (2) at (4.5,0) {$2$};
|
|
||||||
\node[draw, circle] (3) at (6,0) {$3$};
|
|
||||||
\node[draw, circle] (4) at (1.5,0) {$4$};
|
|
||||||
\node[draw, circle] (5) at (3,0) {$5$};
|
|
||||||
\node[draw, circle] (6) at (7.5,0) {$6$};
|
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) edge [bend left=30] (2);
|
|
||||||
\path[draw,thick,->,>=latex] (2) -- (3);
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- (4);
|
|
||||||
\path[draw,thick,->,>=latex] (4) -- (5);
|
|
||||||
\path[draw,thick,->,>=latex] (5) -- (2);
|
|
||||||
\path[draw,thick,->,>=latex] (5) edge [bend right=30] (3);
|
|
||||||
\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]
|
|
||||||
\node[draw, circle] (1) at (1,5) {$1$};
|
|
||||||
\node[draw, circle] (2) at (3,5) {$2$};
|
|
||||||
\node[draw, circle] (3) at (5,5) {$3$};
|
|
||||||
\node[draw, circle] (4) at (1,3) {$4$};
|
|
||||||
\node[draw, circle] (5) at (3,3) {$5$};
|
|
||||||
\node[draw, circle] (6) at (5,3) {$6$};
|
|
||||||
|
|
||||||
\path[draw,thick,->,>=latex] (1) -- (2);
|
|
||||||
\path[draw,thick,->,>=latex] (2) -- (3);
|
|
||||||
\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);
|
|
||||||
|
|
||||||
\node[color=red] at (1,2.3) {$1$};
|
|
||||||
\node[color=red] at (3,2.3) {$1$};
|
|
||||||
\node[color=red] at (5,2.3) {$3$};
|
|
||||||
\node[color=red] at (1,5.7) {$1$};
|
|
||||||
\node[color=red] at (3,5.7) {$2$};
|
|
||||||
\node[color=red] at (5,5.7) {$3$};
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
For example, to calculate the value of $\texttt{paths}(3)$,
|
|
||||||
we can use the formula $\texttt{paths}(2)+\texttt{paths}(5)$,
|
|
||||||
because there are edges from nodes 2 and 5
|
|
||||||
to node 3.
|
|
||||||
Since $\texttt{paths}(2)=2$ and $\texttt{paths}(5)=1$, we conclude that $\texttt{paths}(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 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.
|
|
||||||
For example, in the graph
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (0,0) {$1$};
|
|
||||||
\node[draw, circle] (2) at (2,0) {$2$};
|
|
||||||
\node[draw, circle] (3) at (0,-2) {$3$};
|
|
||||||
\node[draw, circle] (4) at (2,-2) {$4$};
|
|
||||||
\node[draw, circle] (5) at (4,-1) {$5$};
|
|
||||||
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=above:3] {} (2);
|
|
||||||
\path[draw,thick,-] (1) -- node[font=\small,label=left:5] {} (3);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=right:4] {} (4);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:8] {} (5);
|
|
||||||
\path[draw,thick,-] (3) -- node[font=\small,label=below:2] {} (4);
|
|
||||||
\path[draw,thick,-] (4) -- node[font=\small,label=below:1] {} (5);
|
|
||||||
\path[draw,thick,-] (2) -- node[font=\small,label=above:2] {} (3);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
the shortest paths from node 1 may use the following edges:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (0,0) {$1$};
|
|
||||||
\node[draw, circle] (2) at (2,0) {$2$};
|
|
||||||
\node[draw, circle] (3) at (0,-2) {$3$};
|
|
||||||
\node[draw, circle] (4) at (2,-2) {$4$};
|
|
||||||
\node[draw, circle] (5) at (4,-1) {$5$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- node[font=\small,label=above:3] {} (2);
|
|
||||||
\path[draw,thick,->] (1) -- node[font=\small,label=left:5] {} (3);
|
|
||||||
\path[draw,thick,->] (2) -- node[font=\small,label=right:4] {} (4);
|
|
||||||
\path[draw,thick,->] (3) -- node[font=\small,label=below:2] {} (4);
|
|
||||||
\path[draw,thick,->] (4) -- node[font=\small,label=below:1] {} (5);
|
|
||||||
\path[draw,thick,->] (2) -- node[font=\small,label=above:2] {} (3);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
Now we can, for example, calculate the number of
|
|
||||||
shortest paths from node 1 to node 5
|
|
||||||
using dynamic programming:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\node[draw, circle] (1) at (0,0) {$1$};
|
|
||||||
\node[draw, circle] (2) at (2,0) {$2$};
|
|
||||||
\node[draw, circle] (3) at (0,-2) {$3$};
|
|
||||||
\node[draw, circle] (4) at (2,-2) {$4$};
|
|
||||||
\node[draw, circle] (5) at (4,-1) {$5$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- node[font=\small,label=above:3] {} (2);
|
|
||||||
\path[draw,thick,->] (1) -- node[font=\small,label=left:5] {} (3);
|
|
||||||
\path[draw,thick,->] (2) -- node[font=\small,label=right:4] {} (4);
|
|
||||||
\path[draw,thick,->] (3) -- node[font=\small,label=below:2] {} (4);
|
|
||||||
\path[draw,thick,->] (4) -- node[font=\small,label=below:1] {} (5);
|
|
||||||
\path[draw,thick,->] (2) -- node[font=\small,label=above:2] {} (3);
|
|
||||||
|
|
||||||
\node[color=red] at (0,0.7) {$1$};
|
|
||||||
\node[color=red] at (2,0.7) {$1$};
|
|
||||||
\node[color=red] at (0,-2.7) {$2$};
|
|
||||||
\node[color=red] at (2,-2.7) {$3$};
|
|
||||||
\node[color=red] at (4,-1.7) {$3$};
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
\subsubsection{Representing problems as graphs}
|
|
||||||
|
|
||||||
Actually, any dynamic programming problem
|
|
||||||
can be represented as a directed, acyclic graph.
|
|
||||||
In such a graph, each node corresponds to a dynamic programming state
|
|
||||||
and the edges indicate how the states depend on each other.
|
|
||||||
|
|
||||||
As an example, consider the problem
|
|
||||||
of forming a sum of money $n$
|
|
||||||
using coins
|
|
||||||
$\{c_1,c_2,\ldots,c_k\}$.
|
|
||||||
In this problem, we can construct a graph where
|
|
||||||
each node corresponds to a sum of money,
|
|
||||||
and the edges show how the coins can be chosen.
|
|
||||||
For example, for coins $\{1,3,4\}$ and $n=6$,
|
|
||||||
the graph is as follows:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (0) at (0,0) {$0$};
|
|
||||||
\node[draw, circle] (1) at (2,0) {$1$};
|
|
||||||
\node[draw, circle] (2) at (4,0) {$2$};
|
|
||||||
\node[draw, circle] (3) at (6,0) {$3$};
|
|
||||||
\node[draw, circle] (4) at (8,0) {$4$};
|
|
||||||
\node[draw, circle] (5) at (10,0) {$5$};
|
|
||||||
\node[draw, circle] (6) at (12,0) {$6$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (0) -- (1);
|
|
||||||
\path[draw,thick,->] (1) -- (2);
|
|
||||||
\path[draw,thick,->] (2) -- (3);
|
|
||||||
\path[draw,thick,->] (3) -- (4);
|
|
||||||
\path[draw,thick,->] (4) -- (5);
|
|
||||||
\path[draw,thick,->] (5) -- (6);
|
|
||||||
|
|
||||||
\path[draw,thick,->] (0) edge [bend right=30] (3);
|
|
||||||
\path[draw,thick,->] (1) edge [bend right=30] (4);
|
|
||||||
\path[draw,thick,->] (2) edge [bend right=30] (5);
|
|
||||||
\path[draw,thick,->] (3) edge [bend right=30] (6);
|
|
||||||
|
|
||||||
\path[draw,thick,->] (0) edge [bend left=30] (4);
|
|
||||||
\path[draw,thick,->] (1) edge [bend left=30] (5);
|
|
||||||
\path[draw,thick,->] (2) edge [bend left=30] (6);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
Using this representation,
|
|
||||||
the shortest path from node 0 to node $n$
|
|
||||||
corresponds to a solution with the minimum number of coins,
|
|
||||||
and the total number of paths from node 0 to node $n$
|
|
||||||
equals the total number of solutions.
|
|
||||||
|
|
||||||
\section{Successor paths}
|
|
||||||
|
|
||||||
\index{successor graph}
|
|
||||||
\index{functional graph}
|
|
||||||
|
|
||||||
For the rest of the chapter,
|
|
||||||
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
|
|
||||||
one cycle and some paths that lead to it.
|
|
||||||
|
|
||||||
Successor graphs are sometimes called
|
|
||||||
\key{functional graphs}.
|
|
||||||
The reason for this is that any successor graph
|
|
||||||
corresponds to a function that defines
|
|
||||||
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.
|
|
||||||
|
|
||||||
For example, the function
|
|
||||||
\begin{center}
|
|
||||||
\begin{tabular}{r|rrrrrrrrr}
|
|
||||||
$x$ & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\
|
|
||||||
\hline
|
|
||||||
$\texttt{succ}(x)$ & 3 & 5 & 7 & 6 & 2 & 2 & 1 & 6 & 3 \\
|
|
||||||
\end{tabular}
|
|
||||||
\end{center}
|
|
||||||
defines the following graph:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (1) at (0,0) {$1$};
|
|
||||||
\node[draw, circle] (2) at (2,0) {$2$};
|
|
||||||
\node[draw, circle] (3) at (-2,0) {$3$};
|
|
||||||
\node[draw, circle] (4) at (1,-3) {$4$};
|
|
||||||
\node[draw, circle] (5) at (4,0) {$5$};
|
|
||||||
\node[draw, circle] (6) at (2,-1.5) {$6$};
|
|
||||||
\node[draw, circle] (7) at (-2,-1.5) {$7$};
|
|
||||||
\node[draw, circle] (8) at (3,-3) {$8$};
|
|
||||||
\node[draw, circle] (9) at (-4,0) {$9$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- (3);
|
|
||||||
\path[draw,thick,->] (2) edge [bend left=40] (5);
|
|
||||||
\path[draw,thick,->] (3) -- (7);
|
|
||||||
\path[draw,thick,->] (4) -- (6);
|
|
||||||
\path[draw,thick,->] (5) edge [bend left=40] (2);
|
|
||||||
\path[draw,thick,->] (6) -- (2);
|
|
||||||
\path[draw,thick,->] (7) -- (1);
|
|
||||||
\path[draw,thick,->] (8) -- (6);
|
|
||||||
\path[draw,thick,->] (9) -- (3);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
Since each node of a successor graph has a
|
|
||||||
unique successor, we can also define a function $\texttt{succ}(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 $\texttt{succ}(4,6)=2$,
|
|
||||||
because we will reach node 2 by walking 6 steps from node 4:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (1) at (0,0) {$4$};
|
|
||||||
\node[draw, circle] (2) at (1.5,0) {$6$};
|
|
||||||
\node[draw, circle] (3) at (3,0) {$2$};
|
|
||||||
\node[draw, circle] (4) at (4.5,0) {$5$};
|
|
||||||
\node[draw, circle] (5) at (6,0) {$2$};
|
|
||||||
\node[draw, circle] (6) at (7.5,0) {$5$};
|
|
||||||
\node[draw, circle] (7) at (9,0) {$2$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- (2);
|
|
||||||
\path[draw,thick,->] (2) -- (3);
|
|
||||||
\path[draw,thick,->] (3) -- (4);
|
|
||||||
\path[draw,thick,->] (4) -- (5);
|
|
||||||
\path[draw,thick,->] (5) -- (6);
|
|
||||||
\path[draw,thick,->] (6) -- (7);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
A straightforward way to calculate a value of $\texttt{succ}(x,k)$
|
|
||||||
is to start at node $x$ and walk $k$ steps forward, which takes $O(k)$ time.
|
|
||||||
However, using preprocessing, any value of $\texttt{succ}(x,k)$
|
|
||||||
can be calculated in only $O(\log k)$ time.
|
|
||||||
|
|
||||||
The idea is to precalculate all values of $\texttt{succ}(x,k)$ where
|
|
||||||
$k$ is a power of two and at most $u$, where $u$ is
|
|
||||||
the maximum number of steps we will ever walk.
|
|
||||||
This can be efficiently done, because
|
|
||||||
we can use the following recursion:
|
|
||||||
|
|
||||||
\begin{equation*}
|
|
||||||
\texttt{succ}(x,k) = \begin{cases}
|
|
||||||
\texttt{succ}(x) & k = 1\\
|
|
||||||
\texttt{succ}(\texttt{succ}(x,k/2),k/2) & k > 1\\
|
|
||||||
\end{cases}
|
|
||||||
\end{equation*}
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tabular}{r|rrrrrrrrr}
|
|
||||||
$x$ & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \\
|
|
||||||
\hline
|
|
||||||
$\texttt{succ}(x,1)$ & 3 & 5 & 7 & 6 & 2 & 2 & 1 & 6 & 3 \\
|
|
||||||
$\texttt{succ}(x,2)$ & 7 & 2 & 1 & 2 & 5 & 5 & 3 & 2 & 7 \\
|
|
||||||
$\texttt{succ}(x,4)$ & 3 & 2 & 7 & 2 & 5 & 5 & 1 & 2 & 3 \\
|
|
||||||
$\texttt{succ}(x,8)$ & 7 & 2 & 1 & 2 & 5 & 5 & 3 & 2 & 7 \\
|
|
||||||
$\cdots$ \\
|
|
||||||
\end{tabular}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
After this, any value of $\texttt{succ}(x,k)$ can be calculated
|
|
||||||
by presenting the number of steps $k$ as a sum of powers of two.
|
|
||||||
For example, if we want to calculate the value of $\texttt{succ}(x,11)$,
|
|
||||||
we first form the representation $11=8+2+1$.
|
|
||||||
Using that,
|
|
||||||
\[\texttt{succ}(x,11)=\texttt{succ}(\texttt{succ}(\texttt{succ}(x,8),2),1).\]
|
|
||||||
For example, in the previous graph
|
|
||||||
\[\texttt{succ}(4,11)=\texttt{succ}(\texttt{succ}(\texttt{succ}(4,8),2),1)=5.\]
|
|
||||||
|
|
||||||
Such a representation always consists of
|
|
||||||
$O(\log k)$ parts, so calculating a value of $\texttt{succ}(x,k)$
|
|
||||||
takes $O(\log k)$ time.
|
|
||||||
|
|
||||||
\section{Cycle detection}
|
|
||||||
|
|
||||||
\index{cycle}
|
|
||||||
\index{cycle detection}
|
|
||||||
|
|
||||||
Consider a successor graph that only contains
|
|
||||||
a path that ends in a cycle.
|
|
||||||
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?
|
|
||||||
|
|
||||||
For example, in the graph
|
|
||||||
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=0.9]
|
|
||||||
\node[draw, circle] (5) at (0,0) {$5$};
|
|
||||||
\node[draw, circle] (4) at (-2,0) {$4$};
|
|
||||||
\node[draw, circle] (6) at (-1,1.5) {$6$};
|
|
||||||
\node[draw, circle] (3) at (-4,0) {$3$};
|
|
||||||
\node[draw, circle] (2) at (-6,0) {$2$};
|
|
||||||
\node[draw, circle] (1) at (-8,0) {$1$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- (2);
|
|
||||||
\path[draw,thick,->] (2) -- (3);
|
|
||||||
\path[draw,thick,->] (3) -- (4);
|
|
||||||
\path[draw,thick,->] (4) -- (5);
|
|
||||||
\path[draw,thick,->] (5) -- (6);
|
|
||||||
\path[draw,thick,->] (6) -- (4);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
we begin our walk at node 1,
|
|
||||||
the first node that belongs to the cycle is node 4, and the cycle consists
|
|
||||||
of three nodes (4, 5 and 6).
|
|
||||||
|
|
||||||
A simple way to detect the cycle is to walk in the
|
|
||||||
graph and keep track of
|
|
||||||
all nodes that have been visited. Once a node is visited
|
|
||||||
for the second time, we can conclude
|
|
||||||
that the node is the first node in the cycle.
|
|
||||||
This method works in $O(n)$ time and also uses
|
|
||||||
$O(n)$ memory.
|
|
||||||
|
|
||||||
However, there are better algorithms for cycle detection.
|
|
||||||
The time complexity of such algorithms is still $O(n)$,
|
|
||||||
but they only use $O(1)$ memory.
|
|
||||||
This is an important improvement if $n$ is large.
|
|
||||||
Next we will discuss Floyd's algorithm that
|
|
||||||
achieves these properties.
|
|
||||||
|
|
||||||
\subsubsection{Floyd's algorithm}
|
|
||||||
|
|
||||||
\index{Floyd's algorithm}
|
|
||||||
|
|
||||||
\key{Floyd's algorithm}\footnote{The idea of the algorithm is mentioned in \cite{knu982}
|
|
||||||
and attributed to R. W. Floyd; however, it is not known if Floyd actually
|
|
||||||
discovered the algorithm.} walks forward
|
|
||||||
in the graph using two pointers $a$ and $b$.
|
|
||||||
Both pointers begin at a node $x$ that
|
|
||||||
is the starting node of the graph.
|
|
||||||
Then, on each turn, the pointer $a$ walks
|
|
||||||
one step forward and the pointer $b$
|
|
||||||
walks two steps forward.
|
|
||||||
The process continues until
|
|
||||||
the pointers meet each other:
|
|
||||||
\begin{lstlisting}
|
|
||||||
a = succ(x);
|
|
||||||
b = succ(succ(x));
|
|
||||||
while (a != b) {
|
|
||||||
a = succ(a);
|
|
||||||
b = succ(succ(b));
|
|
||||||
}
|
|
||||||
\end{lstlisting}
|
|
||||||
|
|
||||||
At this point, the pointer $a$ has walked $k$ steps
|
|
||||||
and the pointer $b$ has walked $2k$ steps,
|
|
||||||
so the length of the cycle divides $k$.
|
|
||||||
Thus, the first node that belongs to the cycle
|
|
||||||
can be found by moving the pointer $a$ to node $x$
|
|
||||||
and advancing the pointers
|
|
||||||
step by step until they meet again.
|
|
||||||
\begin{lstlisting}
|
|
||||||
a = x;
|
|
||||||
while (a != b) {
|
|
||||||
a = succ(a);
|
|
||||||
b = succ(b);
|
|
||||||
}
|
|
||||||
first = a;
|
|
||||||
\end{lstlisting}
|
|
||||||
|
|
||||||
After this, the length of the cycle
|
|
||||||
can be calculated as follows:
|
|
||||||
\begin{lstlisting}
|
|
||||||
b = succ(a);
|
|
||||||
length = 1;
|
|
||||||
while (a != b) {
|
|
||||||
b = succ(b);
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
\end{lstlisting}
|
|
||||||
|
|
202
chapter17.tex
202
chapter17.tex
|
@ -359,205 +359,3 @@ that create the remaining strongly connected components:
|
||||||
The time complexity of the algorithm is $O(n+m)$,
|
The time complexity of the algorithm is $O(n+m)$,
|
||||||
because the algorithm
|
because the algorithm
|
||||||
performs two depth-first searches.
|
performs two depth-first searches.
|
||||||
|
|
||||||
\section{2SAT problem}
|
|
||||||
|
|
||||||
\index{2SAT problem}
|
|
||||||
|
|
||||||
Strong connectivity is also linked with the
|
|
||||||
\key{2SAT problem}\footnote{The algorithm presented here was
|
|
||||||
introduced in \cite{asp79}.
|
|
||||||
There is also another well-known linear-time algorithm \cite{eve75}
|
|
||||||
that is based on backtracking.}.
|
|
||||||
In this problem, we are given a logical formula
|
|
||||||
\[
|
|
||||||
(a_1 \lor b_1) \land (a_2 \lor b_2) \land \cdots \land (a_m \lor b_m),
|
|
||||||
\]
|
|
||||||
where each $a_i$ and $b_i$ is either a logical variable
|
|
||||||
($x_1,x_2,\ldots,x_n$)
|
|
||||||
or a negation of a logical variable
|
|
||||||
($\lnot x_1, \lnot x_2, \ldots, \lnot x_n$).
|
|
||||||
The symbols ''$\land$'' and ''$\lor$'' denote
|
|
||||||
logical operators ''and'' and ''or''.
|
|
||||||
Our task is to assign each variable a value
|
|
||||||
so that the formula is true, or state
|
|
||||||
that this is not possible.
|
|
||||||
|
|
||||||
For example, the formula
|
|
||||||
\[
|
|
||||||
L_1 = (x_2 \lor \lnot x_1) \land
|
|
||||||
(\lnot x_1 \lor \lnot x_2) \land
|
|
||||||
(x_1 \lor x_3) \land
|
|
||||||
(\lnot x_2 \lor \lnot x_3) \land
|
|
||||||
(x_1 \lor x_4)
|
|
||||||
\]
|
|
||||||
is true when the variables are assigned as follows:
|
|
||||||
|
|
||||||
\[
|
|
||||||
\begin{cases}
|
|
||||||
x_1 = \textrm{false} \\
|
|
||||||
x_2 = \textrm{false} \\
|
|
||||||
x_3 = \textrm{true} \\
|
|
||||||
x_4 = \textrm{true} \\
|
|
||||||
\end{cases}
|
|
||||||
\]
|
|
||||||
|
|
||||||
However, the formula
|
|
||||||
\[
|
|
||||||
L_2 = (x_1 \lor x_2) \land
|
|
||||||
(x_1 \lor \lnot x_2) \land
|
|
||||||
(\lnot x_1 \lor x_3) \land
|
|
||||||
(\lnot x_1 \lor \lnot x_3)
|
|
||||||
\]
|
|
||||||
is always false, regardless of how we
|
|
||||||
assign the values.
|
|
||||||
The reason for this is that we cannot
|
|
||||||
choose a value for $x_1$
|
|
||||||
without creating a contradiction.
|
|
||||||
If $x_1$ is false, both $x_2$ and $\lnot x_2$
|
|
||||||
should be true which is impossible,
|
|
||||||
and if $x_1$ is true, both $x_3$ and $\lnot x_3$
|
|
||||||
should be true which is also impossible.
|
|
||||||
|
|
||||||
The 2SAT problem can be represented as a graph
|
|
||||||
whose nodes correspond to
|
|
||||||
variables $x_i$ and negations $\lnot x_i$,
|
|
||||||
and edges determine the connections
|
|
||||||
between the variables.
|
|
||||||
Each pair $(a_i \lor b_i)$ generates two edges:
|
|
||||||
$\lnot a_i \to b_i$ and $\lnot b_i \to a_i$.
|
|
||||||
This means that if $a_i$ does not hold,
|
|
||||||
$b_i$ must hold, and vice versa.
|
|
||||||
|
|
||||||
The graph for the formula $L_1$ is:
|
|
||||||
\\
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=1.0,minimum size=2pt]
|
|
||||||
\node[draw, circle, inner sep=1.3pt] (1) at (1,2) {$\lnot x_3$};
|
|
||||||
\node[draw, circle] (2) at (3,2) {$x_2$};
|
|
||||||
\node[draw, circle, inner sep=1.3pt] (3) at (1,0) {$\lnot x_4$};
|
|
||||||
\node[draw, circle] (4) at (3,0) {$x_1$};
|
|
||||||
\node[draw, circle, inner sep=1.3pt] (5) at (5,2) {$\lnot x_1$};
|
|
||||||
\node[draw, circle] (6) at (7,2) {$x_4$};
|
|
||||||
\node[draw, circle, inner sep=1.3pt] (7) at (5,0) {$\lnot x_2$};
|
|
||||||
\node[draw, circle] (8) at (7,0) {$x_3$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- (4);
|
|
||||||
\path[draw,thick,->] (4) -- (2);
|
|
||||||
\path[draw,thick,->] (2) -- (1);
|
|
||||||
\path[draw,thick,->] (3) -- (4);
|
|
||||||
\path[draw,thick,->] (2) -- (5);
|
|
||||||
\path[draw,thick,->] (4) -- (7);
|
|
||||||
\path[draw,thick,->] (5) -- (6);
|
|
||||||
\path[draw,thick,->] (5) -- (8);
|
|
||||||
\path[draw,thick,->] (8) -- (7);
|
|
||||||
\path[draw,thick,->] (7) -- (5);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
And the graph for the formula $L_2$ is:
|
|
||||||
\\
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=1.0,minimum size=2pt]
|
|
||||||
\node[draw, circle] (1) at (1,2) {$x_3$};
|
|
||||||
\node[draw, circle] (2) at (3,2) {$x_2$};
|
|
||||||
\node[draw, circle, inner sep=1.3pt] (3) at (5,2) {$\lnot x_2$};
|
|
||||||
\node[draw, circle, inner sep=1.3pt] (4) at (7,2) {$\lnot x_3$};
|
|
||||||
\node[draw, circle, inner sep=1.3pt] (5) at (4,3.5) {$\lnot x_1$};
|
|
||||||
\node[draw, circle] (6) at (4,0.5) {$x_1$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- (5);
|
|
||||||
\path[draw,thick,->] (4) -- (5);
|
|
||||||
\path[draw,thick,->] (6) -- (1);
|
|
||||||
\path[draw,thick,->] (6) -- (4);
|
|
||||||
\path[draw,thick,->] (5) -- (2);
|
|
||||||
\path[draw,thick,->] (5) -- (3);
|
|
||||||
\path[draw,thick,->] (2) -- (6);
|
|
||||||
\path[draw,thick,->] (3) -- (6);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
The structure of the graph tells us whether
|
|
||||||
it is possible to assign the values
|
|
||||||
of the variables so
|
|
||||||
that the formula is true.
|
|
||||||
It turns out that this can be done
|
|
||||||
exactly when there are no nodes
|
|
||||||
$x_i$ and $\lnot x_i$ such that
|
|
||||||
both nodes belong to the
|
|
||||||
same strongly connected component.
|
|
||||||
If there are such nodes,
|
|
||||||
the graph contains
|
|
||||||
a path from $x_i$ to $\lnot x_i$
|
|
||||||
and also a path from $\lnot x_i$ to $x_i$,
|
|
||||||
so both $x_i$ and $\lnot x_i$ should be true
|
|
||||||
which is not possible.
|
|
||||||
|
|
||||||
In the graph of the formula $L_1$
|
|
||||||
there are no nodes $x_i$ and $\lnot x_i$
|
|
||||||
such that both nodes
|
|
||||||
belong to the same strongly connected component,
|
|
||||||
so a solution exists.
|
|
||||||
In the graph of the formula $L_2$
|
|
||||||
all nodes belong to the same strongly connected component,
|
|
||||||
so a solution does not exist.
|
|
||||||
|
|
||||||
If a solution exists, the values for the variables
|
|
||||||
can be found by going through the nodes of the
|
|
||||||
component graph in a reverse topological sort order.
|
|
||||||
At each step, we process a component
|
|
||||||
that does not contain edges that lead to an
|
|
||||||
unprocessed component.
|
|
||||||
If the variables in the component
|
|
||||||
have not been assigned values,
|
|
||||||
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 each variable
|
|
||||||
has been assigned a value.
|
|
||||||
|
|
||||||
The component graph for the formula $L_1$ is as follows:
|
|
||||||
\begin{center}
|
|
||||||
\begin{tikzpicture}[scale=1.0]
|
|
||||||
\node[draw, circle] (1) at (0,0) {$A$};
|
|
||||||
\node[draw, circle] (2) at (2,0) {$B$};
|
|
||||||
\node[draw, circle] (3) at (4,0) {$C$};
|
|
||||||
\node[draw, circle] (4) at (6,0) {$D$};
|
|
||||||
|
|
||||||
\path[draw,thick,->] (1) -- (2);
|
|
||||||
\path[draw,thick,->] (2) -- (3);
|
|
||||||
\path[draw,thick,->] (3) -- (4);
|
|
||||||
\end{tikzpicture}
|
|
||||||
\end{center}
|
|
||||||
|
|
||||||
The components are
|
|
||||||
$A = \{\lnot x_4\}$,
|
|
||||||
$B = \{x_1, x_2, \lnot x_3\}$,
|
|
||||||
$C = \{\lnot x_1, \lnot x_2, x_3\}$ and
|
|
||||||
$D = \{x_4\}$.
|
|
||||||
When constructing the solution,
|
|
||||||
we first process the component $D$
|
|
||||||
where $x_4$ becomes true.
|
|
||||||
After this, we process the component $C$
|
|
||||||
where $x_1$ and $x_2$ become false
|
|
||||||
and $x_3$ becomes true.
|
|
||||||
All variables have been assigned values,
|
|
||||||
so the remaining components $A$ and $B$
|
|
||||||
do not change the variables.
|
|
||||||
|
|
||||||
Note that this method works, because the
|
|
||||||
graph has a special structure:
|
|
||||||
if there are paths from node $x_i$ to node $x_j$
|
|
||||||
and from node $x_j$ to node $\lnot x_j$,
|
|
||||||
then node $x_i$ never becomes true.
|
|
||||||
The reason for this is that there is also
|
|
||||||
a path from node $\lnot x_j$ to node $\lnot x_i$,
|
|
||||||
and both $x_i$ and $x_j$ become false.
|
|
||||||
|
|
||||||
\index{3SAT problem}
|
|
||||||
|
|
||||||
A more difficult problem is the \key{3SAT problem},
|
|
||||||
where each part of the formula is of the form
|
|
||||||
$(a_i \lor b_i \lor c_i)$.
|
|
||||||
This problem is NP-hard, so no efficient algorithm
|
|
||||||
for solving the problem is known.
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ is $O(k)$, which may be slow, because a tree of $n$
|
||||||
nodes may have a chain of $n$ nodes.
|
nodes may have a chain of $n$ nodes.
|
||||||
|
|
||||||
Fortunately, using a technique similar to that
|
Fortunately, using a technique similar to that
|
||||||
used in Chapter 16.3, any value of $\texttt{ancestor}(x,k)$
|
used in Chapter 16.3 (of the full book), any value of $\texttt{ancestor}(x,k)$
|
||||||
can be efficiently calculated in $O(\log k)$ time
|
can be efficiently calculated in $O(\log k)$ time
|
||||||
after preprocessing.
|
after preprocessing.
|
||||||
The idea is to precalculate all values $\texttt{ancestor}(x,k)$
|
The idea is to precalculate all values $\texttt{ancestor}(x,k)$
|
||||||
|
|
34
preface.tex
34
preface.tex
|
@ -2,32 +2,10 @@
|
||||||
\markboth{\MakeUppercase{Preface}}{}
|
\markboth{\MakeUppercase{Preface}}{}
|
||||||
\addcontentsline{toc}{chapter}{Preface}
|
\addcontentsline{toc}{chapter}{Preface}
|
||||||
|
|
||||||
The purpose of this book is to give you
|
This script is based on the Competitive Programmer's Handbook
|
||||||
a thorough introduction to competitive programming.
|
by Antti Laaksonen.
|
||||||
It is assumed that you already
|
|
||||||
know the basics of programming, but no previous
|
|
||||||
background in competitive programming is needed.
|
|
||||||
|
|
||||||
The book is especially intended for
|
It contains the topics relevant for the graph day of the
|
||||||
students who want to learn algorithms and
|
SOI Camp 2021.
|
||||||
possibly participate in
|
Most of the code was modified slightly, and also some minor
|
||||||
the International Olympiad in Informatics (IOI) or
|
adjustments were made on the text.
|
||||||
in the International Collegiate Programming Contest (ICPC).
|
|
||||||
Of course, the book is also suitable for
|
|
||||||
anybody else interested in competitive programming.
|
|
||||||
|
|
||||||
It takes a long time to become a good competitive
|
|
||||||
programmer, but it is also an opportunity to learn a lot.
|
|
||||||
You can be sure that you will get
|
|
||||||
a good general understanding of algorithms
|
|
||||||
if you spend time reading the book,
|
|
||||||
solving problems and taking part in contests.
|
|
||||||
|
|
||||||
The book is under continuous development.
|
|
||||||
You can always send feedback on the book to
|
|
||||||
\texttt{ahslaaks@cs.helsinki.fi}.
|
|
||||||
|
|
||||||
\begin{flushright}
|
|
||||||
Helsinki, August 2019 \\
|
|
||||||
Antti Laaksonen
|
|
||||||
\end{flushright}
|
|
||||||
|
|
Loading…
Reference in New Issue