C Dijkstra 算法 floyd算法 拓展 07-图6 旅游规划 (25分)

07-图6 旅游规划 (25分)

有了一张自驾旅游路线图,你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的,那么需要输出最便宜的一条路径。

输入格式:
输入说明:输入数据的第1行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0~(N−1);M是高速公路的条数;S是出发地的城市编号;D是目的地的城市编号。随后的M行中,每行给出一条高速公路的信息,分别是:城市1、城市2、高速公路长度、收费额,中间用空格分开,数字均为整数且不超过500。输入保证解的存在。

输出格式:
在一行里输出路径的长度和收费总额,数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

输出样例:

3 40

解题
单源最短路径;
除了得到最短路径,还要在此基础上得到最便宜提的路径;

单源最短路径用dijkstra算法
用dijkstra算法时,邻接矩阵表示图更方便;

设置这么多结构函数虽慢,debug和修改代码会更快;
1.设置图结构
把Vertex变为road类型,有distance和cost两个成员变量

#include<iostream>
using namespace std;
typedef struct road Vertex;
typedef int weighttype; 
#define MAX 501
#define INFINITY 65533

struct road{
	int distance;
	int cost;
};

typedef struct  GNode *ptrtoGNode;  //图结构 
struct GNode{
	int Nv;  //节点数
	int Ne;  //边数
	Vertex G[MAX][MAX]; //存放500个序号 
	int start;
	int end; 
};
typedef ptrtoGNode MGraph;

2.初始化图函数

MGraph CreateGraph(int n)
{
	MGraph Graph = new struct GNode;
	Graph->Nv=n;
	Graph->Ne=0;
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
		{
			Graph->G[i][j].cost=INFINITY;
			Graph->G[i][j].distance=INFINITY;
}
	return Graph;
	//距离全部初始化为最大值 
 } 

3.边结构

typedef struct ENode * ptrtoENode;
struct ENode{
	//边结构
	int V1;
	int V2;
	weighttype distance;
	weighttype cost; 
};
typedef ptrtoENode Edge;

4.边插入图中的函数
标准的无向图插法

void InsertEdge(MGraph Graph,Edge E)
{
	Graph->G[E->V1][E->V2].cost=E->cost;
	Graph->G[E->V1][E->V2].distance=E->distance;
	
	Graph->G[E->V2][E->V1].cost=E->cost;
	Graph->G[E->V2][E->V1].distance=E->distance;
}

5.建图函数

MGraph BuildGraph()
{
	int N,M,S,D;
	cin>>N;
	 //s出发,d停止 
	MGraph Graph= CreateGraph(N);
	cin>>Graph->Ne>>Graph->start>>Graph->end;
	if(Graph->Ne!=0)
	for(int i=0;i<Graph->Ne;i++)
	{	
		Edge E=new struct ENode;
		cin>>E->V1>>E->V2>>E->distance>>E->cost;
		InsertEdge(Graph,E);
	}
	return Graph;
}

上面代码进行了输入操作,并构建了图;
可以建立MGraph Graph保存;
下面用Dijkstra函数对图进行处理并输出;
注意点
Dijkstra函数每次都拿未处理过的distance最小值来处理;
需要FindMinDist函数,得到从s出发的最小distance的e的下标;若所有目的地都处理过,返回ERROR值-1即可;

int FindMinDist(MGraph Graph,int dist[],int collected[])
{
	int MinV,V; //下标
	int MinDist = INFINITY;
	
	for(V=0;V<Graph->Nv;V++)
	{	//未收入且小于当前最小值 
		if(collected[V]==false && dist[V]<MinDist)
		{
			MinDist = dist[V];
			MinV=V; //保存下标 
		}
	 }
	 if(MinDist<INFINITY)
	 	return MinV;
	 else return -1; 
 } 

Dijkstra函数
先初始化dist列表,表示从s到每个城市的最短距离;
再初始化cost,表示从s到每个城市的最短距离路径上所画的钱;
再初始化path,表示从s到每个城市的最短距离的路径;
dist初始化为s行的每个元素的distance;无边则INFINITY;
cost初始化为s行的每个元素的cost;无边则INFINITY;
初始化起点距离和cost,都为0,因为自己到自己距离为0,花费为0;
将起点收入collected;
开始循环:
得到s行里没遍历过的城市下标;
收入该城市;
循环该城市到每一个S行城市的距离:
若另一个城市未被收录,且两者距离<INFINITY(有边):
1.判断是否为负边——负边则没法计算最小值——失败退出;(本题不用)

	if(dist[V]+Graph->G[V][i].distance<dist[i])

S从距离最短的V通过V-i的边到i的距离比当前到i的距离小:更新i的距离
也要更新花费的钱;和路径(本题不用保存路径);
若距离相等——而花费更小,也要更新;

bool Dijkstra(MGraph Graph, int s,int e) //S为起点 
{   //传入图,dist为距离列表,path为路径,s为节点
	int collected[MAX]; //表示放入的结点
	int V;
	int dist[MAX];
	int cost[MAX];
	int path[MAX];   //保存路径 
	
	for(int i=0;i<Graph->Nv;i++)
	{
		dist[i]=Graph->G[s][i].distance;   //距离
		cost[i]=Graph->G[s][i].cost;
		if (dist[i]<INFINITY)
			path[i]=s;   //i的上一个为s
		else 
			path[i]=-1;   //没有路 
		collected[i]=false;
	 } 
	 
	 dist[s]=0;   //起点收入集合 
	 cost[s]=0;
	 collected[s]=true;
	 
	 while(1)
	 {
	 	V=FindMinDist(Graph,dist,collected);
	 	if(V==-1)
	 		break;
	 	collected[V]=true;
	 	for( int i=0;i<Graph->Nv;i++)
	 		if(collected[i]==false && Graph->G[V][i].distance<INFINITY){
	 			if(Graph->G[V][i].distance<0)
	 				return false; //有负边——失败
				if(dist[V]+Graph->G[V][i].distance<dist[i])
				{
					dist[i]=dist[V]+Graph->G[V][i].distance; //更新dist[i]
					cost[i]=cost[V]+Graph->G[V][i].cost;
					path[i]=V;
				 }
				 //如果距离相等,花费更小; 
				 if(dist[V]+Graph->G[V][i].distance==dist[i]&&cost[V]+Graph->G[V][i].cost<cost[i]){
				 	dist[i]=dist[V]+Graph->G[V][i].distance; //更新dist[i]
					cost[i]=cost[V]+Graph->G[V][i].cost;
					path[i]=V;
				 } 
			 }
	 } //while 结束 
	 cout<<dist[e]<<" "<<cost[e]<<endl;
	 return true;
 } 

main函数

int main()
{
	MGraph Graph= BuildGraph();
	bool t = Dijkstra(Graph,Graph->start,Graph->end);
	
}

不小心也搞了个floyd的算法,复杂度太高超时了
因为建图规范,所以只要改变main函数和floyd函数就行

void Floyd(MGraph Graph,Vertex D[][MAX]){
	for(int i=0;i<Graph->Nv;i++)
		for(int j=0;j<Graph->Nv;j++)
			D[i][j]=Graph->G[i][j];
			
	for(int k=0;k<Graph->Nv;k++)
		for(int i=0;i<Graph->Nv;i++)
			for(int j=0;j<Graph->Nv;j++)
			{	
				if(D[i][k].distance+D[k][j].distance<D[i][j].distance)
				{
					D[i][j].distance  = D[i][k].distance+D[k][j].distance;
					D[i][j].cost  = D[i][k].cost+D[k][j].cost;
				}
				if(D[i][k].distance+D[k][j].distance==D[i][j].distance
				&&D[i][k].cost+D[k][j].cost<D[i][j].cost)
				{
					D[i][j].distance  = D[i][k].distance+D[k][j].distance;
					D[i][j].cost  = D[i][k].cost+D[k][j].cost;
					}	
			}
}
//此时 
void output(MGraph Graph)
{
	Vertex D[MAX][MAX];
	Floyd(Graph,D);
	//此时D存放最短distance和最短cost;
	cout<<D[Graph->start][Graph->end].distance<<" "<<D[Graph->start][Graph->end].cost;
	
}

//
int main()
{
	MGraph Graph=BuildGraph();
	output(Graph);
}

总结
该题在distance相同的情况下,多加了一个cost的判断;
Dijkstra算法:(有负边则失效) if ( Graph->G[V][W]<0 )
需要collected列表,保存已找到最小值的点;
需要dist列表,保存当前点到每个其他点的最短路径,初始化为INFINITY
计算从S到v的最短路径的方法为

for(int i=0;i<Graph->Nv;i++)
if(dist[i]+Graph->G[i][V] < dist[V])  dist[V]=dist[i]+Graph->G[i][V] ;

复杂度为O(N^2),在于查找最小值的复杂度;
若用最小堆可降至O(NlogN);

Floyd算法:(有负圈则失效) if ( i==j && D[i][j]<0 ) 转一圈回来为负值
得到图的邻接矩阵表示;无边则初始化为最大值INFINITY
在此基础上,得到每两个点的最短距离;
无需collected列表

for(int k=0;k<Graph->Nv;k++)
	for(int i=0;i<Graph->Nv;i++)
		for(int j=0;j<Graph->Nv;j++)
			if(D[i][k]+D[k][j]<D[i][j])
				D[i][j]=D[i][k]+D[k][j];

调用时——要跳过D[n][n],自己到自己最小值为0;
三重循环,得到每两个点的最小值——有动态规划的最优子问题继承;
复杂度O(N^3);

若需要计算cost,Dijkstra算法需要增加一个cost列表,保存起始点到每个其他点的cost;

旅游规划问题推广:
要求数最短路径有多少条
 count[s] = 1;
 如果找到更短路:count[W]=count[V];因为W与V只差一条边,所以两者相同;
 如果找到等长路:count[W]+=count[V];登场路的数量为W前面一个点的路的数量;
要求边数最少的最短路
 count[s] = 0;
 如果找到更短路:count[W]=count[V]+1;
 如果找到等长路:count[W]=count[V]+1;

发布了105 篇原创文章 · 获赞 6 · 访问量 4961

猜你喜欢

转载自blog.csdn.net/BLUEsang/article/details/105441950
今日推荐