PTA 旅游规划_使用邻接表 思路分析及代码解析v1.0
一、前导
1. 需要掌握的知识
- 图的最短路径、Dijkstra最短路算法
2. 题目信息
- 题目来源:PTA / 拼题A
- 题目地址:旅游规划
二、解题思路分析
1. 题意理解
- 单源有权图最短路问题,在本题中有两个最短路维度:距离维度和价格维度
- 已经使用过邻接矩阵的方式解决,详情请参考 PTA 旅游规划(邻接矩阵) 思路分析及代码解析,本文使用邻接表,重点介绍与前文不同的部分
1. 1 输入数据
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
1.2 输出数据
- 输出起点到终点的最短路径及花费的金额。需要注意,当存在多条最短路径时,输出价位最低的那条路径
3 40
2. 思路分析(重点)
- 这道题是一个单源无权图最短路径问题,与哈利·波特的考试属于同类题,同样使用Dijkstra算法解决
- 本题中最短路包含两个维度:两地之间的最短距离 和 两地之间的最低收费,其中最短距离优先级更高
- 从起点执行Dijkstra算法,从最短距离、最低收费两个维度进行代码的实现
三、具体实现
1. 弯路和bug
- 图的最短路问题,多练习才能熟能生巧
2. 代码框架(重点)
2.1 采用的数据结构
- 邻接表:依次构建边、邻接点、邻接表中的头结点、图的数据结构,图中包含头结点数组、头结点数组存储其邻接点
typedef int vertex;
typedef int weight;
struct EdgeStructure
{
vertex V1;
vertex V2;
weight HighwayDist;
weight HighwayCost;
};
typedef struct EdgeStructure *PtrEdge;
typedef struct AdjNodeStructure *PtrAdjNode;
struct AdjNodeStructure
{
vertex vertexNumber;
weight HighwayDist;
weight HighwayCost;
PtrAdjNode Next;
};
struct HeadNode
{
PtrAdjNode AdjNode;
};
typedef struct HeadNode HeadNodeArray[max];
struct GraphStructure
{
int VertexNumber;
int EdgeNumber;
HeadNodeArray headNode;
};
typedef struct GraphStructure *ptrGraph;
- 对于本题,需要三个数组配合Dijkstra( )算法
weight dist[max];
weight cost[max];
bool visited[max];
2.2 程序主体框架
程序伪码描述
int main()
{
1.构建图,通过邻接表存储图的顶点和边
2.执行Dijkstra( )算法
3.打印结果
return 0;
}
2.3 各分支函数
- buildGraph( ):根据输入数据,完成图的构建。createNoEdgeGraph( )子函数用于构建无边图,insertEdge( )子函数用于向图中插入边。创建该函数属于基本功练习
vertex source=maxValue,destination=maxValue;
ptrGraph Graph;
void buildGraph()
{
int VertexNumber;
PtrEdge Edge;
cin>>VertexNumber;
createNoEdgeGraph(VertexNumber);
cin>>Graph->EdgeNumber;
cin>>source>>destination;
for(int i=0;i<Graph->EdgeNumber;i++)
{
Edge=(PtrEdge)malloc(sizeof(struct EdgeStructure));
cin>>Edge->V1>>Edge->V2;
cin>>Edge->HighwayDist>>Edge->HighwayCost;
insertEdge(Edge);
}
return;
}
void insertEdge(PtrEdge Edge)
{
PtrAdjNode Node;
Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure));
Node->Next=Graph->headNode[Edge->V1].AdjNode;
Node->vertexNumber=Edge->V2;
Node->HighwayDist=Edge->HighwayDist;
Node->HighwayCost=Edge->HighwayCost;
Graph->headNode[Edge->V1].AdjNode=Node;
Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure));
Node->Next=Graph->headNode[Edge->V2].AdjNode;
Node->vertexNumber=Edge->V1;
Node->HighwayDist=Edge->HighwayDist;
Node->HighwayCost=Edge->HighwayCost;
Graph->headNode[Edge->V2].AdjNode=Node;
return;
}
void createNoEdgeGraph(int vertexNumber)
{
Graph=(ptrGraph)malloc(sizeof(struct GraphStructure));
Graph->VertexNumber=vertexNumber;
Graph->EdgeNumber=0;
for(int i=0;i<Graph->VertexNumber;i++)
Graph->headNode[i].AdjNode=NULL;
return ;
}
- Dijkstra算法复习:最短路径不是一次成型的,而是逐步成型的,具体步骤包括:
(1) 令S为一个集合(对应上面的visited数组),集合S包括源点source 以及 已经确定了最短路径的顶点v。集合S一开始为空(对应visited数组的元素初值都是false),第一个收录进集合S的是源点
(2) 对任意一个未收录的顶点v,定义 dist[v] 为:源点s到顶点v的最短路径,但该路径仅经过集合S中的顶点,即路径 { s–(vi属于S)–v }得到的最小长度
(3) 除源点外,每次从未收录的顶点中选一个dist最小的收录,按递增(非递减)的顺序生成集合S
(4) 增加一个顶点v进入集合S时,可能影响v的邻接点 i 的dist值(顶点i未在集合S中),需要更新dist为较小值。重复(3),直到符合要求的顶点都收入到集合S中 - 按Dijkstra算法的思路实现即可。使用邻接表存储图时,邻接点的获取还是蛮方便的,效率也高 :)。另外需要注意,当dist相同时,比较cost
void Dijkstra(vertex source)
{
PtrAdjNode adjNode=NULL;
vertex vertexDistMin=Null;
weight distMin=maxValue;
visited[source]=true;
adjNode=Graph->headNode[source].AdjNode;
while(adjNode)
{
if( !visited[adjNode->vertexNumber])
{
dist[adjNode->vertexNumber]=adjNode->HighwayDist;
cost[adjNode->vertexNumber]=adjNode->HighwayCost;
}
adjNode=adjNode->Next;
}
while(true)
{
for(int i=0;i<Graph->VertexNumber;i++)
{
if(distMin>dist[i] && !visited[i])
{
distMin=dist[i];
vertexDistMin=i;
}
}
if(vertexDistMin==Null) break;
visited[vertexDistMin]=true;
adjNode=Graph->headNode[vertexDistMin].AdjNode;
while(adjNode)
{
if(!visited[adjNode->vertexNumber])
{
if(dist[adjNode->vertexNumber] > (dist[vertexDistMin] + adjNode->HighwayDist))
{
dist[adjNode->vertexNumber]=dist[vertexDistMin] + adjNode->HighwayDist;
cost[adjNode->vertexNumber]=cost[vertexDistMin] + adjNode->HighwayCost;
}
else if(dist[adjNode->vertexNumber]==(dist[vertexDistMin] + adjNode->HighwayDist) )
{
if(cost[adjNode->vertexNumber]>cost[vertexDistMin]+adjNode->HighwayCost)
cost[adjNode->vertexNumber]=cost[vertexDistMin]+adjNode->HighwayCost;
}
}
adjNode=adjNode->Next;
}
vertexDistMin=Null;
distMin=maxValue;
}
return;
}
3. 完整编码
- 本文如果对你有帮助,请点赞鼓励 ,谢谢 😊
- 如有建议或意见,欢迎留言
#include <cstdlib>
#include <iostream>
using namespace std;
#define maxValue 501
#define max 500
#define Null -1
typedef int vertex;
typedef int weight;
struct EdgeStructure
{
vertex V1;
vertex V2;
weight HighwayDist;
weight HighwayCost;
};
typedef struct EdgeStructure *PtrEdge;
typedef struct AdjNodeStructure *PtrAdjNode;
struct AdjNodeStructure
{
vertex vertexNumber;
weight HighwayDist;
weight HighwayCost;
PtrAdjNode Next;
};
struct HeadNode
{
PtrAdjNode AdjNode;
};
typedef struct HeadNode HeadNodeArray[max];
struct GraphStructure
{
int VertexNumber;
int EdgeNumber;
HeadNodeArray headNode;
};
typedef struct GraphStructure *ptrGraph;
weight dist[max];
weight cost[max];
bool visited[max];
ptrGraph Graph;
vertex source=maxValue,destination=maxValue;
void buildGraph();
void insertEdge(PtrEdge Edge);
void createNoEdgeGraph(int vertexNumber);
void Dijkstra(vertex source);
void init();
int main()
{
init();
buildGraph();
Dijkstra(source);
cout<<dist[destination]<<" "<<cost[destination];
return 0;
}
void init()
{
for(int i=0;i<max;i++)
{
dist[i]=maxValue;
cost[i]=maxValue;
visited[i]=false;
}
}
void Dijkstra(vertex source)
{
PtrAdjNode adjNode=NULL;
vertex vertexDistMin=Null;
weight distMin=maxValue;
visited[source]=true;
adjNode=Graph->headNode[source].AdjNode;
while(adjNode)
{
if( !visited[adjNode->vertexNumber])
{
dist[adjNode->vertexNumber]=adjNode->HighwayDist;
cost[adjNode->vertexNumber]=adjNode->HighwayCost;
}
adjNode=adjNode->Next;
}
while(true)
{
for(int i=0;i<Graph->VertexNumber;i++)
{
if(distMin>dist[i] && !visited[i])
{
distMin=dist[i];
vertexDistMin=i;
}
}
if(vertexDistMin==Null) break;
visited[vertexDistMin]=true;
adjNode=Graph->headNode[vertexDistMin].AdjNode;
while(adjNode)
{
if(!visited[adjNode->vertexNumber])
{
if(dist[adjNode->vertexNumber] > (dist[vertexDistMin] + adjNode->HighwayDist))
{
dist[adjNode->vertexNumber]=dist[vertexDistMin] + adjNode->HighwayDist;
cost[adjNode->vertexNumber]=cost[vertexDistMin] + adjNode->HighwayCost;
}
else if(dist[adjNode->vertexNumber]==(dist[vertexDistMin] + adjNode->HighwayDist) )
{
if(cost[adjNode->vertexNumber]>cost[vertexDistMin]+adjNode->HighwayCost)
cost[adjNode->vertexNumber]=cost[vertexDistMin]+adjNode->HighwayCost;
}
}
adjNode=adjNode->Next;
}
vertexDistMin=Null;
distMin=maxValue;
}
return;
}
void buildGraph()
{
int VertexNumber;
PtrEdge Edge;
cin>>VertexNumber;
createNoEdgeGraph(VertexNumber);
cin>>Graph->EdgeNumber;
cin>>source>>destination;
for(int i=0;i<Graph->EdgeNumber;i++)
{
Edge=(PtrEdge)malloc(sizeof(struct EdgeStructure));
cin>>Edge->V1>>Edge->V2;
cin>>Edge->HighwayDist>>Edge->HighwayCost;
insertEdge(Edge);
}
return;
}
void insertEdge(PtrEdge Edge)
{
PtrAdjNode Node;
Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure));
Node->Next=Graph->headNode[Edge->V1].AdjNode;
Node->vertexNumber=Edge->V2;
Node->HighwayDist=Edge->HighwayDist;
Node->HighwayCost=Edge->HighwayCost;
Graph->headNode[Edge->V1].AdjNode=Node;
Node=(PtrAdjNode)malloc(sizeof(struct AdjNodeStructure));
Node->Next=Graph->headNode[Edge->V2].AdjNode;
Node->vertexNumber=Edge->V1;
Node->HighwayDist=Edge->HighwayDist;
Node->HighwayCost=Edge->HighwayCost;
Graph->headNode[Edge->V2].AdjNode=Node;
return;
}
void createNoEdgeGraph(int vertexNumber)
{
Graph=(ptrGraph)malloc(sizeof(struct GraphStructure));
Graph->VertexNumber=vertexNumber;
Graph->EdgeNumber=0;
for(int i=0;i<Graph->VertexNumber;i++)
Graph->headNode[i].AdjNode=NULL;
return ;
}
四、参考
- 浙江大学 陈越、何钦铭老师主讲的数据结构