主要内容 #
- 多阶段决策过程
- 最短路径问题
1. 多阶段决策过程 #
20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。
多阶段决策过程,是指这样的一类特殊的活动过程,问题可以按时间顺序分解成若干相互联系的阶段,在每一个阶段都要做出决策,全部过程的决策是一个决策序列。要使整个活动的总体效果达到最优的问题,称为多阶段决策问题。

2. 最短路径问题 #
多段图的最短路径问题为从源点到终点的最小代价路径。如下图所示,每条线上的数字为经过这条路径需要付出的代价,问题为选择怎样的路径才能使所付出的代价最小。

2.1 问题分析 #
根据多段图的性质,我们可以将这种特殊的图结构划分为多个子集,例如如图所示的多段图就可以分成 5 个子集,在图中以 5 种不同颜色来表示。可以明显地看到想要到达某一个子集的顶点,就必须从上一个相邻顶点集的顶点出发,不相邻的子集之间不存在可达的边。
针对这个特性可以推导出解决问题的方法,例如我想要到达顶点 10,那就必须要先到达顶点 8 或者顶点 9。换句话说,到达顶点 10 的最短距离就是在到达顶点8的最短距离 d(1,8) 加上边 (8,10) 的权重,和到达顶点 9 的最短距离 d(1,9) 加上边 (9,10) 的权重中取最小值。因为不相邻的顶点集之间不存在边,所以到达顶点 10 的方式有且仅有上述 2 种。设 C 为某条边的权重,d(m,n) 为从点 m 到点 n 的最短距离,则使用数学语言的描述如下:

再看一个例子,假设要分析到达顶点 8 的最短距离,则只有 3 种情况。即到达顶点 5 的最短距离 d(1,5) 加上边 (5,8) 的权重,和到达顶点 6 的最短距离 d(1,6) 加上边 (6,8) 的权重,和到达顶点 7 的最短距离 d(1,7) 加上边 (7,8) 的权重三者之间取最小值。使用数学语言的描述如下:

根据上面 2 个例子的论述,我们可以把情况从特殊推广到一般情况,设 Cuv 为多段图有向边 <u,v>的权值,源点 s 到终点 v 的最短路径长为 d(s,v),终点为 t,则可以得到该问题的状态转移方程为:

2.2 问题求解 #
例如上述例子中的多段图,可以建立图的邻接矩阵 topography.edges[MAXV][MAXV] 为:

需要一个辅助结构:一维数组 cost[MAXV] 存储到每个顶点的最小开销。例如我们已经求出了到达顶点 1~9 的最小开销如下:

则根据状态转移方程,可以得出从顶点 1 到顶点 10 的最小开销为:

如果我们要找出最短路径,还需要一个一维数组 path[MAXV],用于保存每个顶点的前驱顶点。

找到最短路径的方式为从 path[10] 开始依次访问对应的顶点,访问的次序即为所求的最短路径,直到访问了起点为结束。

2.3 参考程序 #
首先定义图结构的结构体,使用邻接矩阵来存储:
typedef struct //图的定义
{
int edges[MAXV][MAXV]; //邻接矩阵
int n; //顶点数
} MGraph;
定义求解问题需要的辅助结构:
MGraph topography; //保存城市关系的邻接矩阵
int path[MAXV] = {}; //保存到该顶点的最短路径对应的前驱
int min_cost[MAXV] = {}; //保存到每个顶点的最短路径长
要根据测试样例数据建立多段图的邻接矩阵:
MGraph CreateMGraph(int num) //建图
{
MGraph topography;
int n;
int point1, point2;
int value;
//初始化边为不存在
for(int i = 1; i <= num; i++)
{
for(int j = 1; j <= num; j++)
{
topography.edges[i][j] = 0;
}
}
cout << "请输入边数:";
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> point1 >> point2 >> value;
topography.edges[point1][point2] = value;
}
cout << "\n建立的邻接矩阵为:" << endl;
for(int i = 1; i <= num; i++)
{
for(int j = 1; j <= num; j++)
{
printf("%2d ",topography.edges[i][j]);
}
cout >> endl;
}
cout >> endl;
topography.n = num;
return topography;
}
根据状态转移方程求解问题,最后输出最短路径及其开销:
int main()
{
int cities_num = 0; //城市数量
int a_cost; //当前路径的开销
int pre;
cout << "城市数量为:";
cin >> cities_num;
//建图
topography = CreateMGraph(cities_num);
//初始化路径开销
min_cost[1] = 0;
for(int i = 2; i <= topography.n; i++)
{
min_cost[i] = 99999;
}
//依次计算到达所有点的最短路径
for(int i = 2; i <= cities_num; i++)
{
//遍历之前的所有点,计算到达该点的最短路径
for(int j = 1; j < i; j++)
{
if(topography.edges[j][i] != 0) //若路径存在
{
a_cost = min_cost[j] + topography.edges[j][i];
if(a_cost < min_cost[i]) //更新最短路径长
{
min_cost[i] = a_cost;
path[i] = j; //记录前驱顶点
}
}
}
}
//输出到所有顶点的最短路径
for(int i = 1; i <= cities_num; i++)
{
cout << "到顶点" << i << "的最小开销为:" << min_cost[i] << ",路径:" << i;
pre = i;
while(path[pre])
{
cout << "<-" << path[pre];
pre = path[pre];
}
cout << endl;
}
return 0;
}