深入理解動態規劃的一系列問題(2)
在問題1線性搜索里,我們提到了狀態轉移圖模型,因為與通用解法等價,所以基本所有動態規劃問題都可以用圖論的方法求解,很多問題等價于一個最短路徑問題。而本文主要介紹一下通俗的最短路問題——DP第二題:SPA(Shortest Path in an Acyclic Graph)。
對于一個無環圖來講(有環圖后面講到),一個起始節點為s,一個目標節點為t,DPFE為:f(p) = min q{ b(p,q) + f(q)},其中b(p,q)是從p到q的距離,f(p)是從p到t的最短路徑,另外這里的p是q的前驅節點,如果p不是q的直接前驅,那么b(p,q)=∞。而目標函數就是求 f(s),base condition就是f(t)=0。
那么舉例如下:一個有向無環圖,其有四個節點,集合為{s,x,y,t},五條邊,邊集合為{(s,x), (s,y), (x,y), (x,t), (y,t)},長度為{3,5,1,8,5}。那么求從s到t的最短路徑。
首先使每個節點都有到t的邊,如果沒有的,加一條虛擬邊,長度為∞,即(s,t)=∞。DPFE為:f(s) = min{b(s,x)+f(x), b(s,y)+f(y), b(s,t)+f(t)},f(x) = min{b(x,y)+f(y), b(x,t)+f(t)},f(y) = min{b(y,t)+f(t)},f(t)=0。依次代入,得到f(y)=min{5+0}=5,f(x)=min{1+5,8+0}=6,f(s)=min{3+6,5+5,∞+0}=9。
因此最短路徑長度為9,路徑為s->x->y->t。
代碼是經典的最短路代碼,使用鄰接矩陣來表示一個有向無環圖,這里仍然用一個遞歸函數來簡化:
1: /*
2: * Copyright (C) 2013 changedi
3: *
4: * Licensed under the Apache License, Version 2.0 (the "License");
5: * you may not use this file except in compliance with the License.
6: * You may obtain a copy of the License at
7: *
8: * http://www.apache.org/licenses/LICENSE-2.0
9: *
10: * Unless required by applicable law or agreed to in writing, software
11: * distributed under the License is distributed on an "AS IS" BASIS,
12: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13: * See the License for the specific language governing permissions and
14: * limitations under the License.
15: */
16: package com.jybat.dp;
17:
18: import java.util.HashSet;
19: import java.util.Set;
20:
21: public class ShortestPathAcyclic {
22:
23: private static final int INF = Integer.MAX_VALUE;
24:
25: // nodes (s,x,y,t) = {0,1,2,3}, so (s,x) = 3 means d[0][1] = 3
26: private static int[][] distance = {
27: { INF, 3, 5, INF },
28: { INF, INF, 1, 8 },
29: { INF, INF, INF, 5 },
30: { INF, INF, INF, INF }
31: };
32:
33: private static Set<Integer> possibleNextNodes(int node) {
34: Set<Integer> result = new HashSet<Integer>();
35: for (int i = 0; i < distance[node].length; i++) {
36: if (distance[node][i] != INF) {
37: result.add(new Integer(i));
38: }
39: }
40: return result;
41: }
42:
43: public static double f(int currentNode){
44: if(currentNode==3) return 0.0;
45: else{
46: Set<Integer> possibleSuccessors = possibleNextNodes(currentNode);
47: double min = Double.MAX_VALUE;
48: for(Integer d : possibleSuccessors){
49: double dis = distance[currentNode][d]+f(d);
50: if(dis<min) {
51: min = dis;
52: }
53: }
54: return min;
55: }
56: }
57:
58: /**
59: * @param args
60: */
61: public static void main(String[] args) {
62: double shortest = f(0);
63: System.out.println(shortest);
64: }
65:
66: }
其實這個版本的代碼沒有記錄路徑,只求出了最短距離值,路徑的記錄有很多種方式,最直觀的方法是在f函數內部的for循環結束后,把最小距離計算出的對應的后繼節點d保存,然后記錄一個(currentNode,d)這樣的一個節點對,保存到currentNode為key的一個map里,表示對應從currentNode開始到d選定的最短距離的邊,最后把map里所有這些邊都拿出來拼成一個路徑即可。具體代碼就不寫了(因為考慮到影響對動態規劃的理解,記得算法導論里也是把記錄路徑單獨寫一個函數去講解的)。
posted on 2014-03-17 13:40 changedi 閱讀(1559) 評論(0) 編輯 收藏 所屬分類: 算法