|
| 1 | +\chapter{动态规划之最短路径算法} |
| 2 | + |
| 3 | +\begin{introduction} |
| 4 | + \item Dijkstra算法 |
| 5 | + \item Bellman-Ford算法 |
| 6 | + %\item 提要3 |
| 7 | + %\item 提要4 |
| 8 | + %\item 提要5 |
| 9 | +\end{introduction} |
| 10 | + |
| 11 | + |
| 12 | +\section{最短路径定义} |
| 13 | +给的一个带权重的有向图$G=(V,E)$和权重函数$\omega : E\rightarrow R$,该权重函数将每条边映射到实数值的权重上。图中一条路径$p=<v_0,v_1,...,v_k>$的权重$\omega (p)$是构成该路径的所有边的权重之和:$$\omega (p)=\sum\limits_{i=1}^k\omega (v_{i-1},v_i)$$ |
| 14 | + |
| 15 | +定义从结点$u$到结点$v$的最短路径权重 |
| 16 | + |
| 17 | + |
| 18 | +\begin{equation} |
| 19 | +\delta(u,v) = \begin{cases} |
| 20 | +min( \omega (p) : u\stackrel{p}{\longrightarrow} v ) & uv\text{连通} \notag\\ |
| 21 | ++\infty & uv\text{不连通} |
| 22 | +
|
| 23 | +\end{cases} |
| 24 | +\end{equation} |
| 25 | + |
| 26 | + |
| 27 | +从结点$u$到结点$v$的最短路径则定义为任何一条权重为$\omega (p)=\delta(u,v)$的从结点$u$到结点$v$的路径$p$ |
| 28 | + |
| 29 | +对于每个节点,我们定义两个属性。v.d用来记录源节点s到节点v的最短路径权重上界。v.f用来记录节点v的前驱节点。 |
| 30 | + |
| 31 | +定义初始化和松弛两个操作。 |
| 32 | + |
| 33 | +\begin{lstlisting}[caption=初始化和松弛伪代码] |
| 34 | +INITIALIZE-SINGLE-SOURCE(G,s) |
| 35 | +for each vertex v in G.V |
| 36 | + v.d=$\infty$ |
| 37 | + v.f=NULL |
| 38 | +s.d=0 |
| 39 | + |
| 40 | +RELAX(u,v) |
| 41 | +if v.d>u.d+(u,v) |
| 42 | + v.d=u.d+(u,v) |
| 43 | + v.f=u |
| 44 | +\end{lstlisting} |
| 45 | + |
| 46 | +\section{Dijkstra算法} |
| 47 | +Dijkstra算法解决的是一般情况下的单源最短路径问题,边的权重要求为非负值。 |
| 48 | + |
| 49 | +Dijkstra算法在运行过程中关键是维护一个结点的集合$S$。从源节点$s$到该集合中的每个结点之间的最短路径已经找到。该算法重复 |
| 50 | +从结点集$V-S$中选择离源节点最近的结点$u$加入到集合$S$中。然后重新计算源节点到其余各点的距离。如此以往,直到所有点都加入到集合$S$中。 |
| 51 | + |
| 52 | +\begin{lstlisting}[caption=Dijkstra算法伪代码] |
| 53 | +DIJKSTRA(G,S) |
| 54 | +INITIALIZE-SINGLE-SOURCE(G,s) |
| 55 | +S=NULL |
| 56 | +Q=G.V |
| 57 | +while Q!=NULL |
| 58 | + u=EXTRACT-MIN(Q) |
| 59 | + S=S+u |
| 60 | + for each vertex v in G.Adj[u] |
| 61 | + RELAX(u,v) |
| 62 | +\end{lstlisting} |
| 63 | + |
| 64 | +算法运行过程如下: |
| 65 | + |
| 66 | +\begin{figure} |
| 67 | +\centering |
| 68 | +\includegraphics[width=10cm,height=5cm]{image/dijkstra1.png} |
| 69 | +\caption{一个带权有向图} |
| 70 | +\end{figure} |
| 71 | +\begin{figure} |
| 72 | +\centering |
| 73 | +\includegraphics[width=10cm,height=5cm]{image/dijkstra2.png} |
| 74 | +\caption{对应Dijkstar算法执行过程表格} |
| 75 | +\end{figure} |
| 76 | + |
| 77 | +表的左侧是当前的集合。表中每一行的数字为源点通过集合中的点到各点的当前最短路径距离。在表中加粗的数字对应的节点为该轮中将被加入到集合$S$中节点。 |
| 78 | + |
| 79 | +\subsection{时间复杂度} |
| 80 | +算法第1行初始化操作所需时间为$\Theta(V)$,第4行循环一共执|V|次,其中第5行操作所需时间为$O(V)$,第7行的for循环在整个算法执行期间一共执行|E|次,故Dijkstra算法的总运行时间为$O(V^2+E)=O(V^2)$ |
| 81 | + |
| 82 | +\subsection{正确性证明} |
| 83 | +Dijkstra算法使用贪心的策略,每次选择”最近“的节点加入到集合中。下面通过对已经访问的节点数学归纳法证明其正确性。 |
| 84 | + |
| 85 | +\begin{enumerate} |
| 86 | +\item 当只有两个节点的时候,显然成立。 |
| 87 | + |
| 88 | +\item 假设n-1个结点时。现在我们选择一条边(v,u),其中节点u是未访问结点中具有最小的u.d的节点,并且$u.d=v.d+\omega(v,u)$。u.d必定是源点到所有未访问中最短路径长度。因为假如有一条更短的路径,假设k是该路径上第一个未访问的节点,这与假设$k.d>u.d$矛盾,所以u.d必定是源点到所有未访问节点中最短路径长度。类似,如果在没有使用未访问节点情况下存在一条到节点u的更短路径,并且如果该路径上最后一个节点是k,则我们有$u.d=k.d+\omega(k,u)$,与前提矛盾。其余未访问节点同理。 |
| 89 | +\end{enumerate} |
| 90 | + |
| 91 | +综上所述,Dijkstra算法正确。 |
| 92 | + |
| 93 | +\section{Bellman-Ford算法} |
| 94 | +Bellman-Ford算法解决的是一般情况下的单源最短路径问题,边的权重可以为负值。 |
| 95 | + |
| 96 | +由分析可知,s到t的最短路一定是一个简单路径。并且最短路径的边数小于等于n-1(n为节点数)。我们记opt(v,k)为从v到t,边数小于等于k的最短路径。考虑两种情况,一种是最短路径边数小于等于k-1,一种是边数等于k的。 |
| 97 | +则有 |
| 98 | +$$opt(v,k)=min(opt(v,k-1),\min \limits_{(v,w)\in E}(opt(w,k-1)+c(v,w)))$$ |
| 99 | +其中w是与v直接相连的点。 |
| 100 | + |
| 101 | +同理还可以记opt(t,k)为从v到t,边数小于等于k的最短路径。则有 |
| 102 | +$$opt(t,k)=min(opt(t,k-1),\min \limits_{(w,t)\in E}(opt(w,k-1)+c(w,t)))$$ |
| 103 | + |
| 104 | +\begin{lstlisting}[caption=Bellman-Ford算法算法伪代码] |
| 105 | +BELLMAN-FORD(G,s) |
| 106 | + |
| 107 | +INITIALIZE-SINGLE-SOURCE(G,s) |
| 108 | +for i=1 to |G.V|-1 |
| 109 | + for each edge(u,v) in G.E |
| 110 | + RELAX(u,v) |
| 111 | +for each edge(u,v) in G.E |
| 112 | + if v.d>u.d+(u,v) |
| 113 | + return FALSE |
| 114 | +return TRUE |
| 115 | + |
| 116 | +\end{lstlisting} |
| 117 | + |
| 118 | +\subsection{时间复杂度} |
| 119 | +算法第1行初始化操作所需时间为$\Theta (V)$,第2到4行循环的运行时间为$\Theta (E)$,且一共要进行|V|-1次循环,第5到7行的for循环所需时间为$O(E)$,Bellman-Ford算法的总运行时间为$O(VE)$。 |
| 120 | + |
| 121 | +\subsection{正确性证明} |
| 122 | +若某节点和源点不连通,初始化时,除了源点的距离为0外,其他节点的初始化为无穷大。如果不连通,则该节点所在的连通图的任一条边都不会导致更新。 |
| 123 | + |
| 124 | +若节点x点与源点连通。每个点都存在自己的最短路,为$(e_0,e_1,e_2,...,e_k)$。显然,源点只要经过|V|-1条边就可到达任一点。 |
| 125 | + |
| 126 | +现只需证明,对节点v,每次松弛操作,至少有一条最短边$e_i$的距离被找到,除非已经到达v点。 |
| 127 | + |
| 128 | +对于第一次松弛,必定更新和源点s相连的所有出边。由于源点s初始距离是0,和其相连的节点初始距离都是无穷大。而这些相连的出边中,必有一条是节点v的最短路上起始的一条边。则设有节点k,这个节点是节点v最短路上的一共点,由于下一次松弛将更新与k相连的所有点,必能找到下一个点,且也是节点v的最短路径上的一个点。则通过多次松弛可以找出最短路。 |
| 129 | + |
| 130 | +综上所述,Bellman-Ford算法正确。 |
0 commit comments