动态规划范例——驿站马车问题

动态规划范例——驿站马车问题

问题描述

驿站马车问题是阐述动态规划和介绍动态规划术语构建的特殊问题,19世纪中叶,密苏里州的一位淘金者决定去加利福尼亚州淘金,旅程需要乘坐驿站马车,途径那些有遭遇抢到袭击危险的无人乡村,虽然他的出发点和目的地已定,但他有相当多的选择来决定经过哪些州,如图所示

淘金者从A出发,最终到达目的地J州需要经过4个中间阶段
淘金者相当担心自己的安全,想出了一个巧妙的办法,每位驿站马车的乘客都有人寿保险,由于对乘坐任何驿站马车保单的成本都是基于该线路安全性的仔细评估,因而最安全的路线应该是全部人寿保险保单中最便宜的。

问题求解

  • 贪心思想
    为每个连续阶段提供最省钱的路线是一种目光短浅的方法,不能得到全局最优策略,也就是说贪心思想(每一步都选择当前最优的一步走)是在这里是不适用的
  • 枚举法
    虽然枚举法可以得到最优解,但在路线多的情况下将会有极大的工作量
  • 动态规划
    动态规划从原始问题的很小一部分开始,给这个小问题找到最优解,然后初见扩大问题,从前面的问题找到最优解,直到求得全部原始问题的解,得到全局最优解
    从小问题开始,就是淘金者几乎完成了他的旅行,这声最后一个阶段,即为即将到达J州,再扩大这个问题,从每个州到下一个州的最优解,可以从前面重复的结果寻找

动态规划问题的特征

  1. 策略决策,将一个问题分为几个阶段,每个阶段都有一个策略决策,而上述问题可以分为四个阶段,每个阶段都会做出决策,决定选择哪一份人寿保险。
  2. 每个阶段都有与之对应的状态,上述问题中,淘金者旅途的驿站中可以落脚的州就是状态,状态是各种可能的条件。
  3. 每个阶段策略决策的结果都是从当前状态变成下一个阶段开始的状态
  4. 设计求解的过程就是为整个问题找到一个最优策略,每个阶段对每个可能的状态进行最优策略决策的指令
  5. 已知目前状态,对于剩余阶段的最优策略与先前采用的策略无关,最优策略只取决于当前状态,而与如何到达这个状态的过程无关,即最优原理
  6. 求解最后阶段找到最优策略开始
  7. 如果知道n+1阶段的最优策略,就可以确定第n阶段的最优策略,得到一个递推关系,上述的问题的递推关系即d(x)from = min{d(x)from, d(x)to + weight~from, to~}
  8. 通过上述的递推关系,求解过程从终点开始并逐步逆序移动,每次都找到该阶段的最优策略,直到起点,得到的最优策略即为整个问题的解决方案

算法思路

构建有向图,从最后阶段开始,即从终点开始,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

鸣谢

《运筹学导论》

最后

  • 由于博主水平有限,不免有疏漏之处,欢迎读者随时批评指正,以免造成不必要的误解!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章