最短路径问题以及包含过路费的问题--动态规划式的解法
2017-03-05 16:56
1046 查看
求最短路径的dijkstra算法(详细算法原理自行百度,本文简单从动态规划角度思考),可以看成是一个动态规划的方法。
动态规划算法通常基于一个递推公式及一个或多个初始状态。当前子问题的解将由上一次子问题的解推出。
dijkstra虽然没有递推公式,但是满足将大问题分解成子问题,当前问题的解由上一次子问题的解推出。
算法的思想是
使用了三个一维数组,分别是visit[k],pre_node[k],short[k]来分别表示是否经过k点,k点的前驱节点,到达k点的最短距离
初始状态,是所有点出了起点都没经过,所有点的前驱节点都是起点,到达所有点的最短距离是起点直达的距离(没有路径距离就是极大值)。
接着开始循环求子问题。
子问题是先找出没有经过的并且到起点最短距离不是极大值的点i(第一次肯定是离起点最近的点)
如果找不到这个点,跳出循环(结束循环的唯一条件,这样可以避免非联通图出错)
然后分别找出点i的邻点j,判断通过i到j的距离是否小于目前j到起点的距离
如果是,那就更新short[j]和pre_node[k]
最后可以通过pre_node[]来遍历最短路径
上面只有距离唯一一个条件,是一维的,逻辑比较简单
如果每条路径还需要收过路费,就是一个二维的动态规划问题。
问题变为了
到达在符合经费预算的情况下到达目标点的最短距离
或者到达目标点花费最少
算法思想还是使用三个数组,不过是2纬数组,第二维的下标表示的是经费余额
visit[i][j]是否到达节点i并且经费余额为j的情况
short[i][j]表示在经费余额为j的情况下,到达节点i的最短距离
pair
初始状态
visit[i][j]全没经历过 visit[v0][money]=1 v0为原点,money为所有预算
pair[i][j]设为初始值,和起点相邻的并且预算够过路费的标为 (v0,money), 起点的前驱还是起点
其余节点都是起点,余额为-1
short[i][j]都为极大值 short[v0][money] = 0 ,起点本身 距离0,且没花钱
开始循环求解子问题
考虑所有的点以及所有的花费(余额>=0)的情况,
找到满足预算路径最短的点(小于极大值)(一开始是起点的邻点,不一定是最近的点)
如果找不到就跳出循环
找到了 标记为点k,余额为p
找出所有与点k相邻的点ii,
如果余额允许到点ii AND !visit[ii][到达ii后的余额](该情况没经历过) AND 起点经过点k到点ii的距离 < 同等花费下原先的距离(short[ii][p-k到ii的花费])
更新同等花费下原先的距离
更新pre_node[][]
代码如下:
动态规划算法通常基于一个递推公式及一个或多个初始状态。当前子问题的解将由上一次子问题的解推出。
dijkstra虽然没有递推公式,但是满足将大问题分解成子问题,当前问题的解由上一次子问题的解推出。
算法的思想是
使用了三个一维数组,分别是visit[k],pre_node[k],short[k]来分别表示是否经过k点,k点的前驱节点,到达k点的最短距离
初始状态,是所有点出了起点都没经过,所有点的前驱节点都是起点,到达所有点的最短距离是起点直达的距离(没有路径距离就是极大值)。
接着开始循环求子问题。
子问题是先找出没有经过的并且到起点最短距离不是极大值的点i(第一次肯定是离起点最近的点)
如果找不到这个点,跳出循环(结束循环的唯一条件,这样可以避免非联通图出错)
然后分别找出点i的邻点j,判断通过i到j的距离是否小于目前j到起点的距离
如果是,那就更新short[j]和pre_node[k]
最后可以通过pre_node[]来遍历最短路径
int minlength(int (*edge)[7]) { int short_p[7]; int pre_node[7]; int visit[7]; for (int i = 1; i < 7; i++){ visit[i] = 0; pre_node[i] = 1;//起点是1,初始化都从1开始,直接从起点到各个点是老路 short_p[i] = edge[1][i]; } short_p[1] = 0; visit[1] = 1; //for (int v = 0; v < 7; v++){//无法避免不是联通图的情况 while (1){ int min = max_n; int k = -1; for (int i = 2; i < 7; i++){//先找出没进过的并且离起点最近的点 if (!visit[i] && short_p[i] < min){ min = short_p[i]; k = i; } } if (k == -1) break;//找不到就跳出去,因为可能图不连通 else visit[k] = 1; for (int ii = 2; ii < 7; ii++){//以上面找到的点为基准,找出相邻没过的点,检查过这个点到相邻点近还是老路近 if (!visit[ii] && edge[k][ii] + min < short_p[ii]){ short_p[ii] = edge[k][ii] + min; pre_node[ii] = k;//更改前节点 } } } return 0; } int main() { int edge[7][7] = { max_n }; for (int i = 0; i < 7; i++){ for (int j = 0; j < 7; j++) edge[i][j] = max_n; } edge[1][2] = 8; edge[1][5] = 3; edge[2][3] = 1; edge[2][5] = 4; edge[2][6] = 5; edge[3][4] = 4; edge[3][6] = 1; edge[4][6] = 3; edge[5][6] = 2; for (int i = 0; i < 7; i++){ for (int j = i+1; j < 7; j++) edge[j][i] = edge[i][j]; } minlength(edge); }
上面只有距离唯一一个条件,是一维的,逻辑比较简单
如果每条路径还需要收过路费,就是一个二维的动态规划问题。
问题变为了
到达在符合经费预算的情况下到达目标点的最短距离
或者到达目标点花费最少
算法思想还是使用三个数组,不过是2纬数组,第二维的下标表示的是经费余额
visit[i][j]是否到达节点i并且经费余额为j的情况
short[i][j]表示在经费余额为j的情况下,到达节点i的最短距离
pair
<int,int> pre_node[i][j] 用来存放当前情况的前一个情况(节点结合余额)
初始状态
visit[i][j]全没经历过 visit[v0][money]=1 v0为原点,money为所有预算
pair[i][j]设为初始值,和起点相邻的并且预算够过路费的标为 (v0,money), 起点的前驱还是起点
其余节点都是起点,余额为-1
short[i][j]都为极大值 short[v0][money] = 0 ,起点本身 距离0,且没花钱
开始循环求解子问题
考虑所有的点以及所有的花费(余额>=0)的情况,
找到满足预算路径最短的点(小于极大值)(一开始是起点的邻点,不一定是最近的点)
如果找不到就跳出循环
找到了 标记为点k,余额为p
找出所有与点k相邻的点ii,
如果余额允许到点ii AND !visit[ii][到达ii后的余额](该情况没经历过) AND 起点经过点k到点ii的距离 < 同等花费下原先的距离(short[ii][p-k到ii的花费])
更新同等花费下原先的距离
更新pre_node[][]
代码如下:
#include<iostream> #include<algorithm> #include<stack> using namespace std; const int max_n = 65535; int short_p[7][21];//short_p[i][j]表示点i到起点,余额为j的最短距离 int visit[7][21]; int edge[7][7], mny[7][7]; std::pair<int, int> pre_node[7][21]; int minlength(int money) { for (int i = 1; i < 7; i++){ for (int j = 0; j < 21; j++){ visit[i][j] = 0; short_p[i][j] = max_n; pre_node[i][j]=make_pair(1, -1); } if (money - mny[1][i] >= 0){ short_p[i][money - mny[1][i]] = edge[1][i]; pre_node[i][money - mny[1][i]] = make_pair(1, money); } } visit[1][money] = 1;//标记起点 short_p[1][money] = 0;//距起点距离为0,没花钱 pre_node[1][money] = make_pair(1, money); while (true){ int min = max_n; int k = -1, p = -1; for (int i = 2; i < 7; i++){//先找出没进过的并且离起点最近的点 for (int j = 0; j < money; j++){ if (!visit[i][j] && short_p[i][j] < min){ min = short_p[i][j]; k = i; p = j; } } } if (k == -1 || p == -1) break; else visit[k][p] = 1; for (int ii = 2; ii < 7; ii++){//以上面找到的点为基准,找出相邻没过的点,检查过这个点到相邻点近还是老路近 //又足够的余额去相邻的点, 并且没有遇到过同类情况 且 距离更短 if ((p - mny[k][ii] > 0) && !visit[ii][p - mny[k][ii]] && edge[k][ii] + min < short_p[ii][p - mny[k][ii]]){ short_p[ii][p - mny[k][ii]] = edge[k][ii] + min; pre_node[ii][p - mny[k][ii]] = make_pair(k, p);//更改前节点 } } } return 0; } int min_cost_pathprint(int node, int money) { if (node > 6 || node < 1) return -1; cout << "到节点 " << node << " 花费最少为: "; int i = money; for (; i >= 0; i--){ if (short_p[node][i] < max_n) break; } cout << money - i << " 元" << endl; return i; } int min_lengthpath(int node, int money) { if (node > 6 || node < 1) return -1; cout << "到节点 " << node << " 最短距离为: "; int min = max_n; int k; for (int i = 0; i <= money; i++){ if (short_p[node][i] < min){ min = short_p[node][i]; k = i; } } cout << min << endl; return k; } void pathprint(int node, int last) { cout << "路径为:"; std::pair<int, int> pathpair; stack<std::pair<int, int>> path; int dst = node, mm = last; do{ pathpair = make_pair(dst, mm); path.push(pathpair); int dst1 = pre_node[dst][mm].first; int mm1 = pre_node[dst][mm].second; dst = dst1; mm = mm1; } while (pathpair.first != 1); while (path.size() > 1){ cout << '<' << path.top().first << ' ' << path.top().second << '>' << ' '; path.pop(); } cout << '<' << path.top().first << ' ' << path.top().second << '>' << endl; } int main() { for (int i = 0; i < 7; i++){ for (int j = 0; j < 7; j++){ edge[i][j] = max_n; mny[i][j] = max_n; } } //距离 edge[1][2] = 8; edge[1][5] = 3; edge[2][3] = 1; edge[2][5] = 4; edge[2][6] = 5; edge[3][4] = 4; edge[3][6] = 1; edge[4][6] = 3; edge[5][6] = 2; //过路费 mny[1][2] = 1; mny[1][5] = 3; mny[2][3] = 7; mny[2][5] = 6; mny[2][6] = 2; mny[3][4] = 2; mny[3][6] = 4; mny[4][6] = 3; mny[5][6] = 5; for (int i = 0; i < 7; i++){ for (int j = i + 1; j < 7; j++){ edge[j][i] = edge[i][j]; mny[j][i] = mny[i][j]; } } int money = 20; minlength(money); int t1 = min_lengthpath(4,money); if (t1 >= 0) pathprint(4, t1); int t2 = min_cost_pathprint(4, money); if (t2 >= 0) pathprint(4, t2); return 0; } /*收过路费 二维地杰斯特拉 结束*/
相关文章推荐
- 动态规划解决分层图最短路径问题
- 最短带权路径问题的解法::Dijkstra & Floyd
- 动态规划 之最短路径问题
- 我是怎么使用最短路径算法解决动态联动问题的
- 动态规划之所有点对的最短路径问题(Floyd算法)
- 开开心心学算法--街区最短路径问题的二种解法
- 复杂迷宫问题的递归实现以及最短路径
- 动态规划题——最短路径问题
- 动态规划与斐波那契数列问题,最短路径问题
- 最短路径问题(Dijkstra解法)
- 新文章尚邮使用评论 ,包含Gmail的设置以及存在的一些问题
- 邻接矩阵计算最短路径问题的几个发现
- 单源加权图最短路径问题2-BellmanFord算法
- Extjs 树的动态更改加载地址,以及动态加载已有子节点其他节点问题!
- JavaScript动态事件以及闭包问题的解决
- 贪心法求解单源最短路径问题
- EXEC的两种用法以及用动态语句创建临时表的问题
- EXEC的两种用法以及用动态语句创建临时表的问题
- ASP 包含文件中的路径问题和使用单一数据库连接文件的解决方案
- 如何使STATIC控件的背景透明以及动态改变控件文本内容时闪烁的问题