PTA 1131 Subway Map PAT甲级真题 BFS求解

20 篇文章 2 订阅
9 篇文章 0 订阅

PTA-mooc完整题目解析及AC代码库:PTA(拼题A)-浙江大学中国大学mooc数据结构全AC代码与题目解析(C语言)

没啥说的,继续来做最短路的问题。


In the big cities, the subway systems always look so complex to the visitors. To give you some sense, the following figure shows the map of Beijing subway. Now you are supposed to help people with your computer skills! Given the starting position of your user, your task is to find the quickest way to his/her destination.

subwaymap.jpg

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤ 100), the number of subway lines. Then N lines follow, with the i-th (i=1,⋯,N) line describes the i-th subway line in the format:

M S[1] S[2] … S[M]

where M (≤ 100) is the number of stops, and S[i]'s (i=1,⋯,M) are the indices of the stations (the indices are 4-digit numbers from 0000 to 9999) along the line. It is guaranteed that the stations are given in the correct order – that is, the train travels between S[i] and S[i+1] (i=1,⋯,M−1) without any stop.

Note: It is possible to have loops, but not self-loop (no train starts from S and stops at S without passing through another station). Each station interval belongs to a unique subway line. Although the lines may cross each other at some stations (so called “transfer stations”), no station can be the conjunction of more than 5 lines.

After the description of the subway, another positive integer K (≤ 10) is given. Then K lines follow, each gives a query from your user: the two indices as the starting station and the destination, respectively.

The following figure shows the sample map.

samplemap.jpg

Note: It is guaranteed that all the stations are reachable, and all the queries consist of legal station numbers.

Output Specification:

For each query, first print in a line the minimum number of stops. Then you are supposed to show the optimal path in a friendly format as the following:

Take Line#X1 from S1 to S2.
Take Line#X2 from S2 to S3.
......

where Xi’s are the line numbers and Si’s are the station indices. Note: Besides the starting and ending stations, only the transfer stations shall be printed.

If the quickest path is not unique, output the one with the minimum number of transfers, which is guaranteed to be unique.

Sample Input:

4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
3
3011 3013
6666 2001
2004 3001

Sample Output:

2
Take Line#3 from 3011 to 3013.
10
Take Line#4 from 6666 to 1306.
Take Line#3 from 1306 to 2302.
Take Line#2 from 2302 to 2001.
6
Take Line#2 from 2004 to 1204.
Take Line#1 from 1204 to 1306.
Take Line#3 from 1306 to 3001.

题意说明

给出一张城市地铁图,对用户给定的起点和终点站找出一条最短乘车路线。题目看起来很复杂,但本质上仍然还是求最短路问题,是一个无向无权图的单源最短路问题。

输入分析

第一行输入地铁线路数N。

然后下面的N行,每行分别对应一条地铁信息,第i行对应地铁i号线。每一行地铁信息中,第一个数表示该线所有站点数M,然后接着给出M个数,每个数分别为相应站点编号。站点编号为一个4位整数,范围在0000到9999之间。

接下来一行给出用户查询次数K。

后面K行,每一行表示用户一次查询内容,包括起始站和终点站编号。

输出分析

对用户每一次查询,给出搜索到的最佳乘车路线解。

每一个解第一行先给出该路线所需经过路径长度。之后下一行开始给出具体换乘过程,在每一条线上乘车区间应只包括起始站点、终点站和换乘站编号,并且还需输出对应的地铁几号线,具体输出格式参见样例。


解法分析

看到题目首先还是先分析问题的关键点有哪些。这个题目的关键点有两个:

  • 如何存储整个地铁线路图,还要保证能方便地判断每条边所属地铁线?
  • 使用什么样地算法求得最短路?

存储数据

题目给出的地铁数上限是100,每条地铁线的站点数也是100,也就是说最大有10000个站点。如果用邻接矩阵来存怕不是要炸掉,所以我仍然选择了邻接表结构。

这里每一个站点的编号取值范围都在0000-9999之间,所有我直接将邻接表的表头结点数组长度声明为10000个,把各站点信息按照其编号值存到对应位置即可,这样也方便快速的找到每一个站。

每一个站可能在多条地铁线上,而每一条路径只能在一条地铁线上,所以邻接表的每个表头结点后链的每条边信息存储了所在几号地铁线。

遍历算法

因为是一个无权图,所以使用BFS可以实现。网上大多数解法均为DFS实现,但只要将BFS稍作修改也可以应用于这道题。

在求解这道题时,使用一般的BFS可能遇到的问题是,当路径长度相等的两个点同时都与一个新点相关联时,如何保证新点指向换乘次数小的那个站。

我的思路是当BFS队列不为空时,取出一个元素。只在出队时才判断是否为终点。对于该元素的每一个邻接点,如果之前未访问过,则更新对应在辅助数组上的相关值;如果访问过,并且是下一层结点(说明这个下层邻接点被同层的其他结点访问过),那么根据其换乘次数是否比它现存的更小来判断是否更新其值。

此处,为了实时记录换乘次数的变化,我声明了两个辅助数组transferNum(记录换乘次数)和curLine(走到当前站所处的地铁线)。

另外,为了方便输出,我选择的是从终点站向起点站方向反向搜索。


#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define INF 65535
/* ——————无向图的邻接表定义开始—————— */
#define MaxVertexNum 10000
typedef int Vertex;
typedef int DataType;

typedef struct ENode *PtrToENode;
struct ENode{
    Vertex V1, V2;
    int lineNo;     // 这条边在几号线上
};
typedef PtrToENode Edge;

typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode{
    Vertex AdjV;
    int lineNo;     // 这条边在几号线上
    PtrToAdjVNode Next;
};

typedef struct Vnode{
    PtrToAdjVNode FirstEdge;
} AdjList[MaxVertexNum];

typedef struct GNode *PtrToGNode;
struct GNode{
    AdjList G;
};
typedef PtrToGNode LGraph;

LGraph BuildGraph();
void DestoryGraph( LGraph Graph );
void InsertEdge(LGraph Graph, Edge E);
/* ——————无向图的邻接表定义结束—————— */
/* ——————队列定义开始—————— */
#define ERROR -1
typedef int ElementType;
typedef int Position;
struct QNode {
    ElementType *Data;     /* 存储元素的数组 */
    Position Front, Rear;  /* 队列的头、尾指针 */
    int MaxSize;
};
typedef struct QNode *Queue;

Queue CreateQueue(int MaxSize);
void DestoryQueue( Queue Q );
bool IsFull( Queue Q );
bool Enqueue( Queue Q, ElementType X );
bool IsEmpty( Queue Q );
ElementType Dequeue( Queue Q );
void Clear(Queue Q);
/* ——————队列定义结束—————— */
int dist[MaxVertexNum]; // 路径长度
Vertex path[MaxVertexNum];  // 上一步站点编号
int transferNum[MaxVertexNum];  // 换乘次数
int curLine[MaxVertexNum];  // 当前所在地铁线,随着读取的边而改变

void reset();    // 重设辅助数组的初始值
void printRoute(Vertex source);
void query(LGraph graph, Queue queue);

int main()
{
    LGraph graph;
    Queue queue;
    int queryNum, i;
    graph = BuildGraph();
    queue = CreateQueue(MaxVertexNum + 1);
    scanf("%d", &queryNum);
    for (i = 0; i < queryNum; ++i)
        query(graph, queue);
    DestoryGraph(graph);
    DestoryQueue(queue);

    return 0;
}

/* ——————相关数据结构函数实现开始—————— */
LGraph BuildGraph()
{
    LGraph graph; Edge edge;
    int lineNum, stopNum, i, j;
    Vertex preStop, curStop, V;

    graph = (LGraph)malloc(sizeof(struct GNode));
    for (V = 0; V < MaxVertexNum; ++V) graph->G[V].FirstEdge = NULL;
    edge = (Edge)malloc(sizeof(struct ENode));
    scanf("%d", &lineNum);
    for (i = 1; i <= lineNum; ++i) {
        preStop = -1;
        scanf("%d", &stopNum);
        for (j = 0; j < stopNum; ++j) {
            scanf("%d", &curStop);
            if (preStop != -1) {
                edge->V1 = preStop; edge->V2 = curStop; edge->lineNo = i;
                InsertEdge(graph, edge);
            }
            preStop = curStop;
        }
    }
    free(edge);

    return graph;
}

void DestoryGraph( LGraph Graph )
{
    Vertex V;
    PtrToAdjVNode Node;
    if (!Graph) return;
    for (V = 0; V < MaxVertexNum; ++V) {
        while (Graph->G[V].FirstEdge) {
            Node = Graph->G[V].FirstEdge;
            Graph->G[V].FirstEdge = Node->Next;
            free(Node);
        }
    }
    free(Graph);
}

void InsertEdge(LGraph Graph, Edge E)
{
    PtrToAdjVNode NewNode;

    NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode->AdjV = E->V2; NewNode->lineNo = E->lineNo;
    NewNode->Next = Graph->G[E->V1].FirstEdge;
    Graph->G[E->V1].FirstEdge = NewNode;

    NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
    NewNode->AdjV = E->V1; NewNode->lineNo = E->lineNo;
    NewNode->Next = Graph->G[E->V2].FirstEdge;
    Graph->G[E->V2].FirstEdge = NewNode;
}

Queue CreateQueue(int MaxSize)
{
    Queue Q = (Queue)malloc(sizeof(struct QNode));
    Q->Data = (int *)malloc(MaxSize * sizeof(int));
    Q->Front = Q->Rear = 0;
    Q->MaxSize = MaxSize;
    return Q;
}

void DestoryQueue( Queue Q )
{
    if (Q) {
        if (Q->Data)
            free(Q->Data);
        free(Q);
    }
}

bool IsFull( Queue Q )
{
    return ((Q->Rear+1)%Q->MaxSize == Q->Front);
}

bool Enqueue( Queue Q, ElementType X )
{
    if ( IsFull(Q) ) return false;
    else {
        Q->Rear = (Q->Rear+1)%Q->MaxSize;
        Q->Data[Q->Rear] = X;
        return true;
    }
}

bool IsEmpty( Queue Q )
{
    return (Q->Front == Q->Rear);
}

ElementType Dequeue( Queue Q )
{
    if ( IsEmpty(Q) ) return ERROR;
    else  {
        Q->Front =(Q->Front+1)%Q->MaxSize;
        return  Q->Data[Q->Front];
    }
}

void Clear(Queue Q)
{
    Q->Front = Q->Rear = 0;
}
/* ——————相关数据结构函数实现结束—————— */

void reset()
{
    Vertex V;
    for (V = 0; V < MaxVertexNum; ++V) {
        dist[V] = 0;
        path[V] = -1;
        transferNum[V] = -1;
        curLine[V] = 0;
    }
}

void printRoute(Vertex source)
{
    Vertex transfer;
    printf("%d\n", dist[source]);
    while (transferNum[source] >= 0) {
        for (transfer = path[source]; transfer != -1 && curLine[source] == curLine[transfer]; transfer = path[transfer]) ;  // 找到换乘站
        printf("Take Line#%d from %04d to %04d.\n", curLine[source], source, transfer);
        source = transfer;
    }
}

void query(LGraph graph, Queue queue)
{
    Vertex source, destination, V;
    int tmpTransfreNum;
    PtrToAdjVNode edge;

    scanf("%d %d", &source, &destination);
    reset();
    Clear(queue);
    Enqueue(queue, destination);
    while(!IsEmpty(queue)) {
        V = Dequeue(queue);
        if (V == source) break;
        for (edge = graph->G[V].FirstEdge; edge; edge = edge->Next) {
            if (dist[edge->AdjV] == 0 && edge->AdjV != destination) {    // 如果没访问过
                dist[edge->AdjV] = dist[V] + 1;
                path[edge->AdjV] = V;
                curLine[edge->AdjV] = edge->lineNo;
                transferNum[edge->AdjV] = (curLine[V] != edge->lineNo) ? transferNum[V] + 1 : transferNum[V];
                Enqueue(queue, edge->AdjV);
            }
            else if (dist[V] + 1 == dist[edge->AdjV]) { // 如果访问过并且是下一层结点
                tmpTransfreNum = (edge->lineNo == curLine[V]) ? transferNum[V] : (transferNum[V] + 1);
                if (tmpTransfreNum < transferNum[edge->AdjV]) {
                    path[edge->AdjV] = V;
                    transferNum[edge->AdjV] = tmpTransfreNum;
                    curLine[edge->AdjV] = edge->lineNo;
                }
            }
        }
    }
    printRoute(source);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>