/* 多段图的最短路径问题: 定义: 给定有向带权图G(V,E,W),如果把顶点集合V划分成 k个不相交的子集V i ,1≤i ≤k,k≥2,使得E中的任何一条边 (u,v),必有uЄ V i, v ∈ V i+m ,m≥1,则称这样的图为多段图。 决策的第一阶段:确定图中第k-1段的所有顶点到达收点t的花费最小的通路。把这些信息保存 起来,在最后形成最优决策时使用。用数组cost[i]存放顶点i到达手电t的最小花费,用数组path[i] 存放顶点i到达收点t的最小花费通路上的前方顶点编号 决策第二阶段:确定途中第k-2段的所有顶点到收点t的花费最小的通路。这时,利用第一阶段形成的信息来进行 决策,并把决策的结果存放在数组cost和path的相应元素中,依次进行,直到最后确定源点s到收点t的花费最小的 通络。 源点s的path数组中就是最优决策序列 思想: 1设cost[i]表示顶点i到收点t的最短花费 2动态规划方程:cost[i] = min{c[i][j] + cost[j]},1<= j <= n ,并且j != i中 path[i] = j,j是使得c[i][j] + cost[j]最小的j 3 边界条件:cost[n-1] = 0 4 目标状态:cost[0] 算法步骤: 用route[n]存放从源点s出发到达收点t的最短通路上的顶点编号 1)对所有的i,0<=i<n,把cost[i]初始化为最大值,path[i]初始化为-1,cost[n-1]初始化为0 2)令i = n - 2; 3)根据状态迁移方程计算cost[i]和path[i] 4)另i = i -1 ;若i > = 0,转步骤(3),否则转到步骤(5) 5)另i = 0 ,route[i] = 0 6)若route[i] = n-1,算法结束,否则转步骤(7) 7)i = i + 1,route[i] = path[route[i-1],转步骤(6) 输入说明: 首行:顶点个数,顶点从0开始 接下来:每一行分别是:起始结点编号 终止结点编号 权值 输入: 10(顶点个数) 19(边的条数) 0 1 4 0 2 1 0 3 3 1 4 9 1 5 8 2 3 1 2 4 6 2 5 7 2 6 8 3 5 4 3 6 7 4 7 5 4 8 6 5 7 8 5 8 6 6 7 6 6 8 5 7 9 7 8 9 3 输出: 15 0 2 3 5 8 9 */ /* 关键: 1 逆序方式 递推 //二重循环,循环n-1次,每次,求出当前结点的最短花费,外层循环:逆序 for(int i = n-2 ; i >= 0 ; i--) { //内层循环递增求解 int iMin = MAX; for(int j = i+1 ; j <= n-1 ; j++) { //剪枝 if(g_iCost[j] != MAX && g_iMatrix[i][j] != -1) { iRet = g_iCost[j] + g_iMatrix[i][j]; if(iMin > iRet) { iMin = iRet; g_iCost[i] = iMin; //如何记录最短路径 g_iPath[i] = j; 2 顺序方式,递归 //记忆化搜索,也是递归基 if(g_iCost[iEnd] != MAX) { return g_iCost[iEnd]; } //自底向上开始动态规划 int iMin = MAX; int iRet; for(int i = 0 ; i <= n-1 ; i++) { //计算其他状态值,剪枝,cost[i] = min { cost[j] + c[i][j]) if(i > iEnd && g_iMatrix[iEnd][i] != -1) { iRet = dp(i,n) + g_iMatrix[iEnd][i]; if(iMin > iRet) { iMin = iRet; 3设定path[i] = j;j是使cost[i]取得最小值的j //如何记录最短路径 g_iPath[i] = j; 4 1设cost[i]表示顶点i到收点t的最短花费 2动态规划方程:cost[i] = min{c[i][j] + cost[j]},1<= j <= n ,并且j != i中 path[i] = j,j是使得c[i][j] + cost[j]最小的j */ #include <iostream> #include <string.h> #include <string> #include <fstream> #include <vector> using namespace std; const int MAXSIZE = 100; int g_iMatrix[MAXSIZE][MAXSIZE]; int g_iCost[MAXSIZE]; int g_iPath[MAXSIZE]; int g_iRoute[MAXSIZE]; const int MAX = 1000000000; int max(int a,int b) { return a > b ? a : b; } //采用递推的方式做 int dp2(int n) { //自底向上开始动态规划 int iRet; int iCnt = 0; //二重循环,循环n-1次,每次,求出当前结点的最短花费,外层循环:逆序 for(int i = n-2 ; i >= 0 ; i--) { //内层循环递增求解 int iMin = MAX; for(int j = i+1 ; j <= n-1 ; j++) { //剪枝 if(g_iCost[j] != MAX && g_iMatrix[i][j] != -1) { iRet = g_iCost[j] + g_iMatrix[i][j]; if(iMin > iRet) { iMin = iRet; g_iCost[i] = iMin; //如何记录最短路径 g_iPath[i] = j; } } } } return g_iCost[0]; } //因为存在多个阶段,只能用递归做 int dp(int iEnd,int n) { //记忆化搜索,也是递归基 if(g_iCost[iEnd] != MAX) { return g_iCost[iEnd]; } //自底向上开始动态规划 int iMin = MAX; int iRet; for(int i = 0 ; i <= n-1 ; i++) { //计算其他状态值,剪枝,cost[i] = min { cost[j] + c[i][j]) if(i > iEnd && g_iMatrix[iEnd][i] != -1) { iRet = dp(i,n) + g_iMatrix[iEnd][i]; if(iMin > iRet) { iMin = iRet; } } } return g_iCost[iEnd] = iMin; } /*读取文件中的数据*/ bool readData(const string& sFileName) { if(sFileName.empty()) { return false; } ifstream infile; infile.open(sFileName,ios::in); if(infile.bad()) { cout << "打开文件:" << sFileName << ",发生错误" << endl; return false; } string sLine; vector<string> vecTemp; int iBeg,iEnd,iWeight; string sBeg,sEnd,sWeight; while(getline(infile,sLine)) { //if(boost::starts_with(sLine,"***") || sLine.empty()) if(string::npos != sLine.find("*") || sLine.empty()) { continue; } else { vecTemp.clear(); int iFirstBlank = sLine.find_first_of(" "); int iLastBlank = sLine.find_last_of(" "); int iLen = iLastBlank - iFirstBlank - 1; sBeg = sLine.substr(0,iFirstBlank); sEnd = sLine.substr(iFirstBlank + 1, iLen); sWeight = sLine.substr(iLastBlank + 1,sLine.size() - iLastBlank - 1); vecTemp.push_back(sBeg); vecTemp.push_back(sEnd); vecTemp.push_back(sWeight); if(vecTemp.size() != 3) { cout << "读取的文件格式不对,请检查" << endl; return false; } else { iBeg = atoi(vecTemp.at(0).c_str()); iEnd = atoi(vecTemp.at(1).c_str()); iWeight = atoi(vecTemp.at(2).c_str()); cout << "边<" << iBeg << "," << iEnd << ">,权值:" << iWeight << endl; //易错,这里是无向图的邻接矩阵,所以要设置两个无相边的连接值 g_iMatrix[iBeg][iEnd] = iWeight; } } } return true; } /*打印最短路径,path[8] = 9,path[6] = 8,path[5] = 6,path[0] = 2,path[1] = 5,g_iPath[],0,2,3,5,8,9,从Path[0]出发,*/ void printPath(int n) { for(int i = 0 ; i!= n -1 ; i = g_iPath[i] ) { cout << i << " "; } cout << n-1 << endl; } void process1() { memset(g_iMatrix,-1,sizeof(g_iMatrix)); readData("./data.txt"); int iEdgeNum = 19; int n = 10; //初始化最大距离 for(int i = 0 ; i <= n - 1 ; i++) { g_iCost[i] = MAX; } //初始化动态规划数组初始状态 g_iCost[n-1] = 0; memset(g_iPath,-1,sizeof(g_iPath)); cout << dp2(10) << endl; printPath(n); } void process() { int n; while(cin >> n) { int iBeg,iEnd,iDis; memset(g_iMatrix,-1,sizeof(g_iMatrix)); int iEdgeNum ; cin >> iEdgeNum; for(int k = 0 ; k < iEdgeNum ; k++) { cin >> iBeg >> iEnd >> iDis; g_iMatrix[iBeg][iEnd] = iDis; } //初始化最大距离 for(int i = 0 ; i <= n - 1 ; i++) { g_iCost[i] = MAX; } //初始化动态规划数组初始状态 g_iCost[n-1] = 0; memset(g_iPath,-1,sizeof(g_iPath)); dp(0,n); cout << g_iCost[0] << endl; } } int main(int argc,char* argv[]) { process1(); getchar(); return 0; }