算法设计与分析:第四章 动态规划 4.3多段图的最短路径问题

/*
多段图的最短路径问题:
定义: 给定有向带权图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;
}

你可能感兴趣的:(动态规划,算法设计与分析)