動態規劃範例——驛站馬車問題
問題描述
驛站馬車問題是闡述動態規劃和介紹動態規劃術語構建的特殊問題,19世紀中葉,密蘇里州的一位淘金者決定去加利福尼亞州淘金,旅程需要乘坐驛站馬車,途徑那些有遭遇搶到襲擊危險的無人鄉村,雖然他的出發點和目的地已定,但他有相當多的選擇來決定經過哪些州,如圖所示
淘金者從A出發,最終到達目的地J州需要經過4箇中間階段
淘金者相當擔心自己的安全,想出了一個巧妙的辦法,每位驛站馬車的乘客都有人壽保險,由於對乘坐任何驛站馬車保單的成本都是基於該線路安全性的仔細評估,因而最安全的路線應該是全部人壽保險保單中最便宜的。
問題求解
- 貪心思想
爲每個連續階段提供最省錢的路線是一種目光短淺的方法
,不能得到全局最優策略,也就是說貪心思想(每一步都選擇當前最優的一步走)是在這裏是不適用的 - 枚舉法
雖然枚舉法可以得到最優解,但在路線多的情況下將會有極大的工作量 - 動態規劃
動態規劃從原始問題的很小一部分開始,給這個小問題找到最優解,然後初見擴大問題,從前面的問題找到最優解,直到求得全部原始問題的解,得到全局最優解
從小問題開始,就是淘金者幾乎完成了他的旅行,這聲最後一個階段,即爲即將到達J州,再擴大這個問題,從每個州到下一個州的最優解,可以從前面重複的結果尋找
動態規劃問題的特徵
- 策略決策,將一個問題分爲幾個階段,每個階段都有一個策略決策,而上述問題可以分爲四個階段,每個階段都會做出決策,決定選擇哪一份人壽保險。
- 每個階段都有與之對應的狀態,上述問題中,淘金者旅途的驛站中可以落腳的州就是狀態,狀態是各種可能的條件。
- 每個階段策略決策的結果都是從當前狀態變成下一個階段開始的狀態
- 設計求解的過程就是爲整個問題找到一個最優策略,每個階段對每個可能的狀態進行最優策略決策的指令
- 已知目前狀態,對於剩餘階段的最優策略與先前採用的策略無關,最優策略只取決於當前狀態,而與如何到達這個狀態的過程無關,即最優原理
- 求解最後階段找到最優策略開始
- 如果知道n+1階段的最優策略,就可以確定第n階段的最優策略,得到一個遞推關係,上述的問題的遞推關係即d(x)from = min{d(x)from, d(x)to + weight~from, to~}
- 通過上述的遞推關係,求解過程從終點開始並逐步逆序移動,每次都找到該階段的最優策略,直到起點,得到的最優策略即爲整個問題的解決方案
算法思路
構建有向圖,從最後階段開始,即從終點開始,distance數組對應值爲對應頂點距離終點的最短距離,終點初始化爲0,其他爲無窮大,從終點開始向起點移動,每次決策得到當前結點到終點最短距離,直到起點,得到起點到終點的最短距離,動態轉移方程爲distance[adjacentList[i][j].from] = min(adjacentList[i][j].weight + distance[i],distance[adjacentList[i][j].from]) ;
,每個階段取決於下一個階段,每個頂點到終點的最短距離取決於上一個最近鄰接結點,逐個求得最優子結構,最後得到全局最優解
void Graph::dynamicProgramming() {
for (int i = adjacentList.size() - 1; i >= 0; i--) {
for (int j = 0; j < adjacentList[i].size(); j++) {
distance[adjacentList[i][j].from] = min(distance[adjacentList[i][j].from], adjacentList[i][j].weight + distance[i]);
}
}
}
實現代碼
/*
author : eclipse
email : [email protected]
time : Mon Jun 15 18:17:58 2020
*/
#include <bits/stdc++.h>
using namespace std;
struct Edge {
int from;
int weight;
};
class Graph {
private:
static const int INF = 1024;
vector< vector<Edge> > adjacentList;
vector<int> distance;
int vextexNumber;
public:
Graph(int vextexNumber, vector< pair< pair<int, int> , int> > edges);
void dynamicProgramming();
void traverse();
};
Graph::Graph(int vextexNumber, vector< pair< pair<int, int> , int> > edges) {
this->vextexNumber = vextexNumber;
adjacentList.resize(vextexNumber);
distance.resize(vextexNumber);
for (int i = 0; i < vextexNumber; i++) {
distance[i] = INF;
}
distance[vextexNumber - 1] = 0;
for (int i = 0; i < edges.size(); i++) {
adjacentList[edges[i].first.second].push_back((Edge) {edges[i].first.first, edges[i].second});
}
}
void Graph::dynamicProgramming() {
for (int i = adjacentList.size() - 1; i >= 0; i--) {
for (int j = 0; j < adjacentList[i].size(); j++) {
distance[adjacentList[i][j].from] = min(distance[adjacentList[i][j].from], adjacentList[i][j].weight + distance[i]);
}
}
}
void Graph::traverse() {
for (int i = 0; i < distance.size(); i++) {
printf("%d ", distance[i]);
}
}
int main(int argc, char const *argv[]) {
vector< pair< pair<int, int> , int> > edges;
int vextexNumber, edgeNumber;
scanf("%d%d", &vextexNumber, &edgeNumber);
for (int i = 0; i < edgeNumber; i++) {
int from, to, weight;
scanf("%d%d%d", &from, &to, &weight);
edges.push_back(make_pair(make_pair(from, to), weight));
}
Graph *graph = new Graph(vextexNumber, edges);
graph->dynamicProgramming();
graph->traverse();
return 0;
}
輸入數據
10 20
0 1 2
0 2 4
0 3 3
1 4 7
1 5 4
1 6 6
2 4 3
2 5 2
2 6 4
3 4 4
3 5 1
3 6 5
4 7 1
4 8 4
5 7 6
5 8 3
6 7 3
6 8 3
7 9 3
8 9 4
輸入結果
11 11 7 8 4 7 6 3 4 0
鳴謝
最後
- 由於博主水平有限,不免有疏漏之處,歡迎讀者隨時批評指正,以免造成不必要的誤解!