PTA 哈利·波特的考试 思路分析及代码解析

一、前导

1. 需要掌握的知识

  1. 图的最短路径、Dijkstra最短路算法/迪科斯彻最短路算法

2. 题目信息

  1. 题目来源:PTA / 拼题A
  2. 题目地址:哈利·波特的考试

二、解题思路分析

1. 题意理解

  1. 不要被魔法的外衣所欺骗 😊,他本质就是一个无向有权图最短路问题
    (1)各种动物可以抽象为图的顶点
    (2)不同的动物之间若可以变形 说明 图的顶点之间存在边、魔咒的长度就是边的权值
    (3)魔咒倒过来也能用 代表 无向图
    (4)根据猫、鼠、鱼的例子,设猫、鼠、鱼的顶点编号分别为 1 2 3,据下图可知 各顶点的最短路分别为(0,4,6) (4,0,4) (6,4,0) ,随后比较各顶点最短路的最大值,其中的最小值就是答案(有点绕,需要想一想)
    在这里插入图片描述

  2. 解决无向有权图图最短路问题,可以使用Dijkstra最短路算法,只要正确实现 Dijkstra( ) ,就可以AC本题

1. 1 输入数据

6 11	//图的顶点数为6,边数为11;如下11行为边的详情
3 4 70 //顶点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

1.2 输出数据

  1. 找到各个顶点作为源点时的最短路,随后比较各顶点最短路的最大值,输出其中最小值的顶点编号 及 最小值
  2. 若图不连通,输出0;若存在多个相同的最小值,输出顶点编号最小的
4 70 

2. 思路分析(重点)

  1. 使用二维数组存储有权无向图
  2. 通过Dijkstra算法求得图中各个源点的最短路
  3. 进行数据比较,获取题目要求的数据并打印

三、具体实现

1. 弯路和bug

  1. 熟能生巧,最短路算法需要多编码练习

2. 代码框架(重点)

2.1 采用的数据结构

  1. 通过二维数组存储数据
#define max 101 //依据题意,最多100个顶点
int a[max][max];
  1. 对于本题,需要三个数组配合实现Dijkstra( )
int dist[v];  //顶点v到源点s的路径长度
int result[]; //存储源点s的最短路径中的最大值
bool check[]; //表示顶点是否已存储到最短路径顶点集合中

2.2 程序主体框架

               程序伪码描述
int main()
{	
	1.创建图
	2.通过循环语句,对各顶点依次使用Dijkstra算法
	3.进行数据比较
	return 0;
}

2.3 各分支函数

  1. Dijkstra( ) 核心函数,在正式编码之前,需要简单说明下Dijkstra算法,对于Dijkstra算法来说,最短路径不是一次成型的,而是逐步成型的,他的具体步骤包括:
    (1) 令S为一个集合,该集合包括源点source 以及 已经确定了最短路径的顶点v
    (2) 对任意一个未收录的顶点v,定义 dist[v] 为:源点s到顶点v的最短路径,但该路径仅经过集合S中的顶点,即路径 { s–>(vi属于S)–>v }的最小长度
    (3) 每次从未收录的顶点中选一个dist最小的收录(按递增的顺序生成集合S):贪心算法
    (4) 增加一个顶点v进入集合S时,可能影响另一个顶点w的dist值,所以需要进行判定。
    举个栗子:以下图为例,先收录源点1进入集合S[1],此时的最短路为(0,4,6),按照算法,再收录源点2进入集合S[1,2],此时的最短路为(0,4,5) :由于2的加入,顶点3到源点1的最短路径发生了变化
    在这里插入图片描述
    依据上述步骤进行具体实现
int Dijkstra(int Source)
{
	int dist_mini=infinite, dist_max=0 , vertex=-1; //每次从未收录的顶点中选一个dist最小的收录,变量vertex表示dist最小的顶点 
	check[Source]=true;//将源点收录到集合S中,check[]对应集合S,数组元素默认值为false
	dist[Source] = 0; //源点到源点的长度设为0,以便区分 
	
	for(int i=1;i<=Nodes;i++) //更新dist[v] , 顶点v到Source的最短路径, dist[]默认值均为 无穷大
	{
		if( isEdge(Source,i)  ) //第一次收录的是源点 
			dist[i]=a[Source][i];
	} 
	
	while(true) //while循环开始收录非源点的顶点到集合S,直到所有连通的顶点都收录进去 
	{
		for(int i=1;i<=Nodes;i++) //找到 未收录的顶点中 dist最小的顶点 
		{
			if(dist[i] < dist_mini && !check[i] ) 
			{
				dist_mini=dist[i];
				vertex=i;
			} 		
		}
		
		if(vertex==-1) break; //所有满足条件的顶点都已经收录,退出循环 
		
		check[vertex]=true; //新的顶点已经收录,然后更新与其有边的顶点dist 
		for(int i=1;i<=Nodes;i++)
		{
			if( isEdge(vertex,i) && dist[vertex]+a[vertex][i] < dist[i] )  //core sentence!
				dist[i]=dist[vertex]+a[vertex][i];
		}
		
		dist_mini=infinite, vertex=-1; //初始化 	
	} 
	
	for(int i=1;i<=Nodes;i++)
		if(dist[i]>dist_max)
			dist_max=dist[i];
	
	return dist_max;
}
  1. CreateGraphic( ) 创建图,属于基本功
  2. Default( ) 每个顶点执行一次 Dijkstra( ) 后,需要初始化dist数组和check数组
void CreateGraphic()
{
	int first,last,value;
	cin>>Nodes>>Edge;
	for(int i=1;i<=Nodes;i++)
	{
		for(int j=1;j<=Nodes;j++)
			a[i][j]=Null;
	}
	for(int k=1;k<=Edge;k++)
	{
		cin>>first>>last>>value;
		a[first][last]=value;
		a[last][first]=value;
	}
	Default();
	return;
}

void Default()
{
	for(int i=1;i<=Nodes;i++)
	{
		dist[i]=infinite;   //顶点i到源点的距离 ,不能设定为0,若为0,则无法判定出源点(设源点到源点为0) 
		check[i]=false; //默认都不在集合S中 
	}
	return;	
}

3. 完整编码

#include<iostream>
using namespace std;

#define max 101
#define Null 0
#define infinite 99999999

int a[max][max];
int dist[max];
int result[max];
bool check[max];
int Nodes,Edge;

void CreateGraphic(); 
void Default(); //初始化 
int Dijkstra(int Source);
bool isEdge(int i, int j);

int main()
{
	int vertex, result_min=infinite;//result_min 比较各顶点最短路的最大值,找到其中最小值 
		
	CreateGraphic();
	
	for(int i=1;i<=Nodes;i++) 
	{
		result[i]=Dijkstra(i);
		Default();
	} 
		
	for(int i=1;i<=Nodes;i++)
	{
		if(result[i]==infinite) //结果为无穷大,说明不是连通图
		{
			cout<<"0";
			return 0;
		}
			
		if(result[i]<result_min)
		{
			result_min=result[i];
			vertex=i;
		}
	}
	
	cout<<vertex<<" "<<result_min;

	
	return 0;
} 

bool isEdge(int i, int j)
{
	if(!a[i][j])
		return false;
	else 
		return true;
}

int Dijkstra(int Source)
{
	int dist_mini=infinite, dist_max=0 , vertex=-1; //每次从未收录的顶点中选一个dist最小的收录,变量vertex表示dist最小的顶点 
	check[Source]=true;//将源点收录到集合S中,check[]对应集合S,数组元素默认值为false
	dist[Source] = 0; //源点到源点的长度设为0,以便区分 
	
	for(int i=1;i<=Nodes;i++) //更新dist[v] , 顶点v到Source的最短路径, dist[]默认值均为 无穷大
	{
		if( isEdge(Source,i)  ) //第一次收录的是源点 
			dist[i]=a[Source][i];
	} 
	
	while(true) //while循环开始收录非源点的顶点到集合S,直到所有连通的顶点都收录进去 
	{
		for(int i=1;i<=Nodes;i++) //找到 未收录的顶点中 dist最小的顶点 
		{
			if(dist[i] < dist_mini && !check[i] ) 
			{
				dist_mini=dist[i];
				vertex=i;
			} 		
		}
		
		if(vertex==-1) break; //所有满足条件的顶点都已经收录,退出循环 
		
		check[vertex]=true; //新的顶点已经收录,然后更新与其有边的顶点dist 
		for(int i=1;i<=Nodes;i++)
		{
			if( isEdge(vertex,i) && dist[vertex]+a[vertex][i] < dist[i] )  //core sentence!
				dist[i]=dist[vertex]+a[vertex][i];
		}
		
		dist_mini=infinite, vertex=-1; //初始化 	
	} 
	
	for(int i=1;i<=Nodes;i++)
		if(dist[i]>dist_max)
			dist_max=dist[i];
	
	return dist_max;
}

void CreateGraphic()
{
	int first,last,value;
	cin>>Nodes>>Edge;
	for(int i=1;i<=Nodes;i++)
	{
		for(int j=1;j<=Nodes;j++)
			a[i][j]=Null;
	}
	for(int k=1;k<=Edge;k++)
	{
		cin>>first>>last>>value;
		a[first][last]=value;
		a[last][first]=value;
	}
	Default();
	return;
}

void Default()
{
	for(int i=1;i<=Nodes;i++)
	{
		dist[i]=infinite;   //顶点i到源点的距离 ,不能设定为0,若为0,则无法判定出源点(设源点到源点为0) 
		check[i]=false; //默认都不在集合S中 
	}
	return;	
}


四、参考

  1. 浙江大学 陈越、何钦铭老师主讲的数据结构
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值