北京地铁线路查询

Dijkstra Algorithm 迪杰斯特拉算法

北京地铁线路查询是对最短路径算法的应用,此代码运用迪杰斯特拉算法求出两站之间的一条最短路径
如果读者想要求出所有的最短路径,可参考弗洛伊德算法

【问题描述】编写一个程序实现北京地铁最短乘坐(站)线路查询,输入为起始站名和目的站名,输出为从起始站到目的站的最短乘坐站换乘线路。注:1. 要求采用Dijkstra算法实现;2)如果两站间存在多条最短路径,找出其中的一条就行。
北京地铁线路查询_第1张图片
【输入形式】文件bgstations.txt为数据文件(可从课程网站中课程信息处下载),包含了北京地铁的线路及车站信息。其格式如下:
<地铁线路总条数>
<线路1> <线路1站数>
<站名1> <换乘状态>
<站名2> <换乘状态>

<线路2> <线路2站数>
<站名1> <换乘状态>
<站名2> <换乘状态>

说明:文件第一行为地铁总线路数;第二行第一个数为某条地铁线线号(如,1为1号线),第二个数为该条地铁线的总站数(如1号线共有23站),两数之间由一个空格分隔;第三行两个数据分别为地铁站名及换乘状态(0为非换乘站,1为换乘站),两数据间由一个空格分隔;以下同,依次为该线地铁其它站信息。在一条线路信息之后是下条地铁线路信息,格式相同。若某条地铁线为环线,则首站与末站信息相同(如北京地铁2号线,首站信息“西直门 1” ,末站信息为“西直门 1”)。例如本题提供的bgstations.txt文件(可从课程网站中课程信息处下载)内容如下:
13
1 23
苹果园 0
古城 0
八角游乐园 0
八宝山 0
玉泉路 0
五棵松 0
万寿路 0
公主坟 1
军事博物馆 1
木樨地 0
南礼士路 0
复兴门 1
西单 1

2 19
西直门 1
积水潭 0
鼓楼大街 1

西直门 1

该文件表明当前北京地铁共有13条线路(不含郊区线路),接着为每条线路信息。

打开当前目录下文件bgstations.txt,读入地铁线路信息,并从标准输入中读入起始站和目的站名(均为字符串,各占一行)。

【输出形式】
输出从起始站到目的站的乘坐信息,要求乘坐站数最少。换乘信息格式如下:
SSN-n1(m1)-S1-n2(m2)-…-ESN
其中:SSN和ESN分别为起始站名和目的站名;n为乘坐的地铁线路号,m为乘坐站数。
【样例输入】
西土城
北京西站
【样例输出】
西土城-10(1)-知春路-13(2)-西直门-4(2)-国家图书馆-9(4)-北京西站
(或西土城-10(1)-知春路-13(2)-西直门-2(1)-车公庄-6(2)-白石桥南-9(3)-北京西站)
【样例说明】
打开文件bgstations.txt,读入地铁线路信息,并从标准输入中读入查询起始站名为“西土城”,目的站名为“北京西站”。程序运行结果两站间最少乘坐站数的乘坐方式为“西土城站乘坐10号线1站至知春路站换乘13号线乘坐2站至西直门站换乘4号线乘坐2站至国家图书馆站换乘9号线乘坐4站至北京西站”。本样例存在两条最少站数的乘坐方式,只要找出一条就可以。
【评分标准】对于同一个起始站和目的站,如果存在多条最少站数的乘坐方式,只要找出其中一条就可以。测试点全通过得满分。

代码思路

  1. 初始化图,将所有的线路连接成图。方法是:将每一站作为顶点,即图的顶点,存成结构体。结构体的内容为:
    1. 站的名称
    2. 是否为换乘站
    将两个线路的连线(距离),也就是图的边同样作为结构体存储。结构体的内容为:
    3. 站与站间的距离
    4. 两个站所在的线路号
    随后我们初始化图。

    一维数组BGvertex用来存储每一个顶点,数组下标即为顶点的编号。二维数组BGweights存储图的边,是无向图的邻接矩阵。对于每一个站,首先我们判断它是否为换乘站,如果不是,则将它存储到BGvertex中并返回数组下标(这个顶点的编号),将v2赋值给v1(作用是将此顶点作为下一个顶点的起始点),并继续扫描顶点。如果是换乘站,则在BGvertex中寻找这个换乘站,如果没找到,则按照非换乘站处理,如果找到,则直接返回它的下标(顶点编号)。这样我们遍得到了北京地铁站的无向图。

    这里需要注意的是,在得到图之后,我们需要对图进行“初始化”,即将两顶点之间没有边的邻接矩阵的wei初始化为INFINITY(无穷大),将两个顶点不在任意一条线路的邻接矩阵的ino初始化为-1,进而方便后续迪杰斯特拉算法的运行。

  2. 迪杰斯特拉算法
    本代码在原算法的基础上,只增加了当找到对应顶点后跳出的步骤。如果读者对迪杰斯特拉算法不了解,可先自行学习,这里不再阐述。

  3. 按格式输出
    我们通过回溯spath的方式(spath记录的是每一个顶点的前驱,详细请见迪杰斯特拉算法),并将线路中的每一个地铁站倒序存进数组board中。我们首先存入头站点,随后循环输出其余站点。在循环中需要判断是否换乘。我们将每一次循环的字符串strcat连接到path的尾部,最后输出。

详细代码如下

#include 
#include 
#include 
#define MAXNUM 512 //地铁最大站数
#define MAXLEN 16
#define INFINITY 32767

struct station
{
    char sname[MAXLEN];
    int ischange; //是否为换乘站,0为不是换乘站,1为是换乘站
};

struct weight
{
    int wei; //两个站之间的权重,即相差的站数
    int ino //两个顶点所在的线号
};

struct station BGvertex[MAXNUM]; //地铁网的顶点
struct weight BGweights[MAXNUM][MAXNUM]; //网络图权重
int Vnum = 0; //实际地铁总站数

int addVertex(struct station p)
{
    if (!p.ischange) //不是换乘站
    {
        BGvertex[Vnum++] = p;
        return Vnum - 1;
    }
    else
    {
        int i;
        for (i = 0; i < Vnum; i++) //查找换乘站
            if (!strcmp(p.sname, BGvertex[i].sname))
                return i;
        BGvertex[Vnum++] = p;
        return Vnum - 1;
    }
}

void Init_Map()
{
    FILE *fp;
    int i, j, snum, Ino, Inum, v1, v2;
    struct station st;
    if ((fp = fopen("bgstations.txt", "r")) == NULL)
        printf("Cannot open this file!\n");
    fscanf(fp, "%d", &snum);
    for (i = 0; i < snum; i++)
    {
        fscanf(fp, "%d %d", &Ino, &Inum);
        v1 = v2 = -1;
        for (j = 0; j < Inum; j++)
        {
            fscanf(fp, "%s %d", st.sname, &st.ischange);
            v2 = addVertex(st); //将st加入到顶点数组内并返回数组下标,其中我们用BGvertex数组的下标来表示顶点的编号
            if (v1 != -1)
            {
                BGweights[v1][v2].wei = BGweights[v2][v1].wei = 1; //相邻顶点权重为1
                BGweights[v1][v2].ino = BGweights[v2][v1].ino = Ino; //同一线路,线路号相同
            } //无向图,v1->v2和v2->v1都要存在邻接矩阵里
            v1 = v2;
        }
    }
    for (i = 0; i < Vnum; i++)
    {
        for (j = 0; j < Vnum; j++)
        {
            if (!BGweights[i][j].wei)
                BGweights[i][j].wei = INFINITY;
            if (!BGweights[i][j].ino)
                BGweights[i][j].ino = -1;
        }
    }
    fclose(fp);
}

void PrintPath(int v0, int v1, int spath[])
{
    char path[80] = {0}, buf[80];
    int board[80], bcount = 0, line = -1, sc = 0;
    int i;
    do{
        board[bcount++] = v1;
    }while((v1 = spath[v1]) != v0);
    board[bcount++] = v0;
    line = BGweights[board[bcount - 1]][board[bcount - 2]].ino;
    sprintf(buf, "%s-%d(", BGvertex[board[bcount - 1]].sname, line);
    strcpy(path, buf);
    sc = 1;
    for (i = bcount - 2; i > 0; i--, sc++)
    {
        if (BGweights[board[i]][board[i - 1]].ino != line) //换乘,新的线路号
        {
            line = BGweights[board[i]][board[i - 1]].ino;
            sprintf(buf, "%d)-%s-%d(", sc, BGvertex[board[i]].sname, line);
            strcat(path, buf);
            sc = 0;
        }
    }
    sprintf(buf, "%d)-%s\n", sc, BGvertex[board[i]].sname);
    strcat(path, buf);
    puts(path);
}

int main()
{
    char ap1[MAXLEN], ap2[MAXLEN];
    scanf("%s %s", ap1, ap2);
    int v0 = -1, v1 = -1, i;
    Init_Map();
    for (i = 0; i < Vnum; i++)
    {
        if (!strcmp(ap1, BGvertex[i].sname))
            v0 = i;
        if (!strcmp(ap2, BGvertex[i].sname))
            v1 = i;
    }
    int spath[MAXNUM] = {0};
    Dijkstra(v0, v1, spath);
    PrintPath(v0, v1, spath);
    return 0;
}

你可能感兴趣的:(数据结构)