最短路径问题以及包含过路费的问题--动态规划式的解法

来源:互联网 发布:英语交流翻译软件 编辑:程序博客网 时间:2024/06/06 04:52

求最短路径的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[]来遍历最短路径

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;}/*收过路费 二维地杰斯特拉 结束*/
0 0
原创粉丝点击