動態規劃-最短路徑

動態規劃作爲一個非常成熟的算法思想,適合用動態規劃來解決的問題需要滿足一個模型三個特徵。

模型是:多階段決策最優解模型
特徵是:
1. 最優子結構
最優子結構指的是,問題的最優解包含子問題的最優解。反過來說就是,我們可以通過子問題的最優解,推導出問題的最優解。如果我們把最優子結構,對應到我們前面定義的動態規劃問題模型上,那我們也可以理解爲,後面階段的狀態可以通過前面階段的狀態推導出來。
2. 無後效性
無後效性有兩層含義,第一層含義是,在推導後面階段的狀態的時候,我們只關心前面階段的狀態值,不關心這個狀態是怎麼一步一步推導出來的。第二層含義是,某階段狀態一旦確定,就不受之後階段的決策影響。無後效性是一個非常“寬鬆”的要求。只要滿足前面提到的動態規劃問題模型,其實基本上都會滿足無後效性。
3. 重複子問題
這個概念比較好理解。前面一節,我已經多次提過。如果用一句話概括一下,那就是,不同的決策序列,到達某個相同的階段時,可能會產生重複的狀態。

數組含義代表:[0,0]到[0,1]的距離是3,[0,0]到[1,0]的距離是2,求取從[0,0]到達某點的最短路徑。

{1, 3, 5, 9},
{2, 1, 3, 4},
{5, 2, 6, 7},
{6, 8, 4, 3} 

解決動態規劃問題。一般有兩種方法:

  • 動態轉移表法

這種方法一般需要我們先畫出一個狀態表。狀態表一般都是二維的,所以可以把它想象成二維數組。其中,每個狀態包含三個變量,行、列、數組值。我們根據決策的先後過程,從前往後,根據遞推關係,分階段填充狀態表中的每個狀態。最後,我們將這個遞推填表的過程,翻譯成代碼,就是動態規劃代碼了。

比如說最短路徑,需要一步步遞推到達某個點的最短路徑(狀態),進行填表。

 	public int minDistDP(int[][] matrix, int n) {
        int[][] states = new int[n][n];
        int sum = 0;
        for (int j = 0; j < n; ++j) { // 初始化 states 的第一行數據
            sum += matrix[0][j];
            states[0][j] = sum;
        }
        sum = 0;
        for (int i = 0; i < n; ++i) { // 初始化 states 的第一列數據
            sum += matrix[i][0];
            states[i][0] = sum;
        }
        for (int i = 1; i < n; ++i) {
            for (int j = 1; j < n; ++j) {
                states[i][j] =
                        matrix[i][j] + Math.min(states[i][j - 1], states[i - 1][j]);
            }
        }
        return states[n - 1][n - 1];
    }
  • 動態轉移方程法

狀態轉移方程法有點類似遞歸的解題思路。我們需要分析,某個問題如何通過子問題來遞歸求解,也就是所謂的最優子結構。根據最優子結構,寫出遞歸公式,也就是所謂的狀態轉移方程。有了狀態轉移方程,代碼實現就非常簡單了。一般情況下,我們有兩種代碼實現方法,一種是遞歸加“備忘錄”,另一種是迭代遞推。

最短路徑的狀態轉移方程是:

min_dist(i, j) = w[i][j] + min(min_dist(i, j-1), min_dist(i-1, j))

人的大腦不適宜過分深究思考遞歸的實現,需要多加鍛鍊。可以想象遍歷文件夾文件的方式。

 	private int[][] matrix = {{1, 3, 5, 9}, {2, 1, 3, 4}, {5, 2, 6, 7}, {6, 8, 4, 3}};
    private int[][] mem = new int[4][4];//橫豎存儲4個數字

    public int minDist(int i, int j) { // 調用 minDist(n-1, n-1);
        if (i == 0 && j == 0) {
            return matrix[0][0];
        }
        if (mem[i][j] > 0) {
            return mem[i][j];
        }
        int minLeft = Integer.MAX_VALUE;
        if (j - 1 >= 0) {
            minLeft = minDist(i, j - 1);
        }
        int minUp = Integer.MAX_VALUE;
        if (i - 1 >= 0) {
            minUp = minDist(i - 1, j);
        }
        int currMinDist = matrix[i][j] + Math.min(minLeft, minUp);
        mem[i][j] = currMinDist;
        return currMinDist;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章