最短路径算法,Dijkstra算法,floyd算法 07-图4 哈利·波特的考试 (25 分)

07-图4 哈利·波特的考试 (25 分)
哈利·波特要考试了,他需要你的帮助。这门课学的是用魔咒将一种动物变成另一种动物的本事。例如将猫变成老鼠的魔咒是haha,将老鼠变成鱼的魔咒是hehe等等。反方向变化的魔咒就是简单地将原来的魔咒倒过来念,例如ahah可以将老鼠变成猫。另外,如果想把猫变成鱼,可以通过念一个直接魔咒lalala,也可以将猫变老鼠、老鼠变鱼的魔咒连起来念:hahahehe。

现在哈利·波特的手里有一本教材,里面列出了所有的变形魔咒和能变的动物。老师允许他自己带一只动物去考场,要考察他把这只动物变成任意一只指定动物的本事。于是他来问你:带什么动物去可以让最难变的那种动物(即该动物变为哈利·波特自己带去的动物所需要的魔咒最长)需要的魔咒最短?例如:如果只有猫、鼠、鱼,则显然哈利·波特应该带鼠去,因为鼠变成另外两种动物都只需要念4个字符;而如果带猫去,则至少需要念6个字符才能把猫变成鱼;同理,带鱼去也不是最好的选择。

输入格式:
输入说明:输入第1行给出两个正整数N (≤100)和M,其中N是考试涉及的动物总数,M是用于直接变形的魔咒条数。为简单起见,我们将动物按1~N编号。随后M行,每行给出了3个正整数,分别是两种动物的编号、以及它们之间变形需要的魔咒的长度(≤100),数字之间用空格分隔。

输出格式:
输出哈利·波特应该带去考场的动物的编号、以及最长的变形魔咒的长度,中间以空格分隔。如果只带1只动物是不可能完成所有变形要求的,则输出0。如果有若干只动物都可以备选,则输出编号最小的那只。

输入样例:
6 11
3 4 70
1 2 1
5 4 50
2 6 50
5 6 60
1 3 70
4 6 60
3 6 80
5 1 100
2 4 60
5 2 80
结尾无空行

首先建立邻接矩阵,代码如下:

#include<iostream>
#include<cmath>
using namespace std;
const int INF = 32767;
const int MaxSize = 501;
typedef int ElemType;
//结点信息
typedef struct {
	int no;//顶点编号
	ElemType data;//其他信息
}VertexType;
typedef struct {
	int edges[MaxSize][MaxSize];
	int n, e;
	VertexType vexs[MaxSize];
}MatGraph;//邻接矩阵表达形式

这题显然是一道最短路径问题,
首先最短路径算法有两种
单源最短路径算法为Dijkstra算法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码如下:

//Dijkstra算法描述,单源最短路径算法
void Dijkstra(MatGraph g, int v)
{
	int distanc[MaxSize], path[MaxSize];//分别记录v到各点的最短距离以及对应路径
	int S[MaxSize];//记录顶点是否被处理过,即是否已经用来更新最短路径s[i]=1则表示已经更新
	//把距离和路径初始化,与v邻接的点距离赋值为该条边的长度,不邻接的赋值为无穷
	//邻接点的路径path[i]=v,因为他的下一个顶点就是v,暂时的最短路径就是直接到v
	//非邻接点的路径path[i]=-1,表示没有返回的最短路径的相应点。
	for (int i = 0;i < g.n;++i)
	{
		distanc[i] = g.edges[v][i];
		S[i] = 0;//表示暂时还没有顶点被处理过,不在S里面
		if (g.edges[v][i] < INF)
			path[i] = v;
		else
			path[i] = -1;
	}
	S[v] = 1;path[v] = 0;//这里path[v]=0是否不需要?,S[v]=1表示v在S里面了,v已经处理了
	int temp;
	//循环直到把所以点放入S中,得到最终最短路径
	for (int i = 0;i < g.n - 1;++i)
	{
		int MINdis = INF;//把最短距离初始化为最大
		for (int j = 0;j < g.n;++j)//找出不在S中且距离v最近的顶点u
			if (S[j] == 0 && distanc[j] < MINdis)
			{
				temp = j;
				MINdis = distanc[j];//这样不停置换就可以得到最小的distanc对应的temp
		    }
		S[temp] = 1;//然后堆temp的邻接点进行操作,更新最短路径
		for(int j=0;j<g.n;++j)
			if (S[j] == 0)//表示没有被处理过的,可以用来更新的j点
				if (g.edges[temp][j] < INF && distanc[temp] + g.edges[temp][j] < distanc[j])
				{
					distanc[j] = distanc[temp] + g.edges[temp][j];
					path[j] = temp;
					//这里找到temp的邻接点,然后判断是否存在更短的路径,可以的话进行调换    
				}
	}
	//到这里所以点已经访问完毕,可以进行输出
	Dispath(g, distanc, path, S, v);
}
//最短路径输出函数
void Dispath(MatGraph g, int distanc[], int path[], int S[], int v)
{
	//输出从顶点v出发的所有最短路径
	int k,d,path1[MaxSize];
	for(int i=0;i<g.n;i++)
		if (S[i] == 1 && i != v)
		{
			cout << "从顶点" << v << "到顶点" << i << "的最短路径为:" << distanc[i] << endl;
			d = 0;
			path1[d] = i;//把路径上最后一个点放在第一位,然后逆序输出;
			k = path[i];
			if (k == -1)
				cout << "无路径" << endl;//表示i点和v直接没有路径
			else
			{
				while (k != v)//在path中找到路径,并且将其放到path1中
				{
					++d;
					path1[d] = k;
					k = path[k];
				}
				++d;path1[d] = v;//把起始结点放到最后一位
				for (int j = d;j >= 0;--j)
					cout << path[j] << " ";//把路径结点倒序输出
				cout << endl;
			}
		}
}

多源最短路径算法如下:
在这里插入图片描述
在这里插入图片描述
代码如下:

//Floyd算法,多源最短路径算法,把每对顶点之间的最短路径算出来
void Floyd(MatGraph g)
{
	int distanc1[MaxSize][MaxSize], path[MaxSize][MaxSize];
	//首先初始化路径和距离
	for(int i=0;i<g.n;++i)
		for (int j = 0;j < g.n;++j)
		{
			distanc1[i][j] = g.edges[i][j];
			if (i != j && g.edges[i][j] < INF)
				path[i][j] = i;
			else
				path[i][j] = -1;
		}
	//然后依次考察所以顶点
	for(int k=0;k<g.n;++k)
		for(int i=0;i<g.n;++i)
			for(int j=0;j<g.n;++j)
				if (distanc1[i][j] > distanc1[i][k] + distanc1[k][j])
				{
					distanc1[i][j] = distanc1[i][k] + distanc1[k][j];
					path[i][j] = path[k][j];
					//即在i,j之间插入了一个k,构成了另一条更短的最短路径,所以j的前一个元素变成k
				}
	Dispath1(g, distanc1, path);
}
//Floyd算法的输出
void Dispath1(MatGraph g, int distanc1[][MaxSize], int path[][MaxSize])
{
	int k,path1[MaxSize],d;//path1可以定义在外部,不需要每次循环之后更新
	//因为每一次循环输出时,会用到的path1[d]的值都会重新赋值,没有重新赋值的也不会用到
	//上面那个Dijkstra算法也是一样
	for(int i=0;i<g.n;++i)
		for (int j = 0;j < g.n;++j)
		{
			if (distanc1[i][j] != INF && i != j)//若i与j之间存在路径
			{
				cout << "从" << i << "到" << j << "的路径为:";
				k = path[i][j];
				d = 0;path1[d] = j;//把终点放在路径数组首位,然后逆序输出
				while (d != -1 && k != i)//还有路径可走并且没有到达终点i
				{
					d++;
					path1[d] = k;
					k = path[i][k];
				}
				d++;path1[d] = i;
				for (int s = d;s >= 0;--s)
					cout << path1[s] << " ";
				cout << endl;
				cout << "路径长度为:" << distanc1[i][j] << endl;
			}
		}
}

改题目其实是floyd算法的一个变形,比较简单,但是要注意堆栈溢出,还有就是只输出一个动物,记得break;代码如下:

#include<iostream>
using namespace std;
const int INF = 65535;//这个数是int的最大值
const int MaxSize = 100;
int distanc1[MaxSize][MaxSize];
int path[MaxSize][MaxSize];//这些都应该定义在全局里面,才不会溢出
typedef int ElemType;
//结点信息
typedef struct {
	int no;//顶点编号
	ElemType data;//其他信息
}VertexType;
typedef struct {
	int edges[MaxSize][MaxSize];
	int n, e;
	VertexType vexs[MaxSize];
}MatGraph;//邻接矩阵表达形式
MatGraph g;//应该定义在全局,这样才不会堆栈溢出
//Floyd算法,多源最短路径算法,
void Dispath1()//这里不应该把path,distance1,还有g等参数放进去,因为放进去之后在主函数里面
//调用时还是会占有主函数堆栈的空间,到时候数据一大又会堆栈溢出
{
	int ans[MaxSize] = { 0 };
	for (int i = 0;i < g.n;++i)
		for (int j = 0;j < g.n;++j)
		{
			if (ans[i] <= distanc1[i][j])
			{
				ans[i] = distanc1[i][j];
			}
		}
	int Min = INF;
	for (int i = 0;i < g.n;++i)
	{
		if (Min > ans[i])
		{
			Min = ans[i];
		}
	}
	if (Min == INF)
		cout << "0" << endl;
	else
	{
		for (int j = 0;j < g.n;++j)
		{
			if (ans[j] == Min)
			{
				cout << j + 1 << " " << ans[j] << endl;
				break;//这一步忘记了的话后面两个测试点过不去
			}
		}
	}
}
void Floyd()//这里也是一样,不能有占空间较大的参数
{
	//首先初始化路径和距离
	for(int i=0;i<g.n;++i)
		for (int j = 0;j < g.n;++j)
		{
			distanc1[i][j] = g.edges[i][j];
			if (i != j && g.edges[i][j] < INF)
				path[i][j] = i;
			else
				path[i][j] = -1;
		}
	//然后依次考察所以顶点
	for(int k=0;k<g.n;++k)
		for(int i=0;i<g.n;++i)
			for(int j=0;j<g.n;++j)
				if (distanc1[i][j] > distanc1[i][k] + distanc1[k][j])
				{
					distanc1[i][j] = distanc1[i][k] + distanc1[k][j];
					path[i][j] = path[k][j];
					//即在i,j之间插入了一个k,构成了另一条更短的最短路径,所以j的前一个元素变成k
				}
	Dispath1();
}
int main()
{
	int N, M;
	cin >> N >> M;
	g.n = N;
	g.e = M;
	for(int i=0;i<MaxSize;++i)
		for (int j = 0;j < MaxSize;++j)
		{
			if (i == j)
				g.edges[i][j] = 0;
			else
				g.edges[i][j] = INF;
		}
	int animal1, animal2, magic;
	for (int i = 0;i < M;++i)
	{
		cin >> animal1 >> animal2 >> magic;
		g.edges[animal1-1][animal2-1] = magic;
		g.edges[animal2-1][animal1-1] = magic;
	}
	Floyd();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值