最短路径问题——这道题绝对经典(华为2014年校招机试题)
问题描述高级题:地铁换乘
已知2条地铁线路,其中A为环线,B为东西向线路,线路都是双向的。经过的站点名分别如下,两条线交叉的换乘点用T1、T2表示。编写程序,任意输入两个站点名称,输出乘坐地铁最少需要经过的车站数量(含输入的起点和终点,换乘站点只计算一次)。
地铁线A(环线)经过车站:A1??A2??A3??A4??A5??A6??A7??A8??A9??T1??A10??A11??A12??A13??T2??A14??A15??A16??A17??A18
地铁线A(直线)经过车站:B1??B2??B3??B4??B5??T1??B6??B7??B8??B9??B10??T2??B11??B12??B13??B14??B15
输入:输入两个不同的站名
输出:输出最少经过的站数,含输入的起点和终点,换乘站点只计算一次
思路解析:
我不知道大家看到这道题是怎么思考的,我看到这道题第一反映就是他要我求最短路径,而最短路径几种常用的算法是Dijkstra,Floyd和A*算法,而A*算法尤为常用(我对A*算法比较熟悉,之前做过这个项目),所以我不佳思索的就是开始用A*算法写了。先开始通过名字构建图....因为说实话这个图没有什么规律,必须一点点构建,特别是节点出的连接,构建图我大概花了20分钟,然后开始写A*算法。A*算法一个最关键的问题就是求代价值,而实际上这个问题并不好求代价值,但也不是不能求。我花了30分钟设计出代价值的函数,于是我开始反思,我觉得这样下去,加上我把A*算法重头写一遍,加上 调试应该还要1个小时左右的时间。可是华为这道题给我们的总共时间只有一个小时,于是我开始否认我的这一条思路了。
然后我又很自然的想到了,对于这种题用图的深度遍历,他将会得出多条路径,然后我可以选取最短的一条。这貌似是一个不错的方法,可是这样做的前提是我必须还要构建一个图出来,这样下来构建图依旧要花费20分钟,只有40分感觉时间比较紧张,可能对与容错处理和健壮性仍旧会考虑不佳。我又开始否认我这条思路。
我想,到底有没有一种编码效率更高的方法可以在最短时间内完成这个代码呢?我再次仔细读题,他只需要我输出最少经过的站数,并不对每一个站是哪个站做了要求,所以一个很笨但是却思维清晰的方法浮现在我脑海中。
分析可知:
这个图就出现了。我们可以把所有的线路分成五段和两个点,总共七中情况。所以如果我穷举所有的情况也就是7*7=49种,单实际上有很多情况我们只要把起点和终点交换一下就可以作为另一种情况了。接下来下面的代码就出现了
用时:总49分钟。起始只要思路清晰一种一种情况的写是不容易出错的,虽然这个代码维护起来比较麻烦。
#include <math.h>
/*
判断这个名字在哪段路上
*/
int Which(string name)
{
if(name[0] == 'B')//假设name1为起点,现在只考虑起点
{
if(atoi(&name[1]) <=5 && atoi(&name[1]) >= 1)
{//B为第一个路段
return 1;
}
else if(atoi(&name[1]) <=10 && atoi(&name[1]) >= 6)
{
return 3;
}
else if(atoi(&name[1]) <=15 && atoi(&name[1]) >= 11)
{
return 5;
}
}
else if(name[0] == 'A')
{
if(atoi(&name[1]) <=13 && atoi(&name[1]) >= 10)
return 2;
else if((atoi(&name[1]) <=9 && atoi(&name[1]) >= 1) || (atoi(&name[1]) <=15 && atoi(&name[1]) >= 11))
return 4;
}
else if(name[0] == 'T')
{
if(atoi(&name[1]) == 1)
return 6;
else if(atoi(&name[1]) <=13)
return 7;
}
else
return 0;//不在这个路上的站点
}
#define T1_1(id) (6-id) //1->T1的站数
#define T2_1(id) (T1_1(id)+5) //1->T2的站数
#define T1_2(id) (id-8) //2->T1
#define T2_2(id) (15-id) //2->T2
#define T1_3(id) (id-4) //3->T1
#define T2_3(id) (12-id) //3->T2
#define T1_5(id) (6+id-10) //5->T1
#define T2_5(id) (id-9) //5->T2
//接口函数
int BestRouting(string name1,string name2)
{
//分为5段和两个节点考虑
//B1-B5 1
//A10-A13 2
//B6-B10 3
//A1-A9+A14-A18 4
//B11-B15 5
//T1 6
//T2 7
//1->(2,5,3,4)的路径很容易分析
int id1 = atoi(&name1[1]),id2 = atoi(&name2[1]) ;
if(Which(name1) == 1)//起点在1路段上
{
if(Which(name2) == 1)//都在1路段
return abs(id1 - id2)+1;
else if(Which(name2) == 2)//结束路段在2
return T1_1(id1)+id2-9;
else if(Which(name2) == 3)//借宿路段在3
return T1_1(id1)+id2-5;
else if(Which(name2) == 4)
{
if(id2 < 17)
return T2_1(id1)+id2-13;
else if(id2 == 18)
return T1_1(id1)+11;
else
{
return T1_1(id1)+10-id2;
}
}
else if(Which(name2) == 5)
{
return T2_1(id1)+id2-10;
}
else if(Which(name2) == 6)
return T1_1(id1);
else if(Which(name2) == 7)
return T2_1(id1);
}
else if(Which(name1) == 2)
{
if(Which(name2) == 1)
return BestRouting(name2,name1);
else if(Which(name2) == 2)
return abs(id1-id2)+1;
else if(Which(name2) == 3)
{
if(id1 <= 11 && id2 <= 8)
return T1_2(id1)+T1_3(id2)-1;
if(id1 >= 12 && id2 > 8)
return T2_2(id1)+T2_3(id2)-1;
if(id1 <= 11 && id2 > 8)
return T2_2(id1)+T2_3(id2)-1;
else
return T1_2(id1)+T1_3(id2)-1;
}
else if(Which(name2) == 4)
{
if(id2 >= 3 && id2 <= 9)
return T1_2(id1)+10-id2;
else if(id2 < 3)
return T2_2(id1)+id2-1+6;
else
return T2_2(id1)+id2-13;
}
else if(Which(name2) == 5)
return T2_2(id1)+id2-10;
else if(Which(name2) == 6)
return T1_2(id1);
else if(Which(name2) == 7)
return T2_2(id1);
}
else if(Which(name1) == 3)
{
if(Which(name2) == 1)
return BestRouting(name2,name1);
else if(Which(name2) == 2)
return BestRouting(name2,name1);
else if(Which(name2) == 3)
return abs(id1-id2)+1;
else if(Which(name2) == 4)
{
if(id2 >= 3 && id2 <= 9)
return T1_3(id1)+10-id2;
else if(id2 < 3)
return T2_3(id1)+id2-1+6;
else
return T2_3(id1)+id2-13;
}
else if(Which(name2) == 5)
{
return id2-id1+2;
}
else if(Which(name2) == 6)
return T1_3(id1);
else if(Which(name2) == 7)
return T2_3(id1);
}
else if(Which(name1) == 4)
{
if(Which(name2) == 5)
{
if(id2 > 5 && id2 < 14)
return 15-id1+id2-10;
else if(id2 <= 5)
return id1+7+T2_5(id2);
else
return id1-13+T2_5(id2);
}
else if(Which(name2) == 6)
{
if(id1 <= 18 && id1 >= 14)
return 6+id1-13;
else if(id1 <= 9)
return 11-id2;
else
return 19-id1+10;
}
else if(Which(name2) == 7)
{
if(id1 >= 5 && id1 <= 9)
return 6+id1-8;
else if(id1 < 5)
return id1+6;
else
return id1 - 12;
}
else if(Which(name2) == 4)
{
if((id1 <= 9 && id1 >= 8) && (id2 <= 15 && id2 >= 14))补充:软件开发 , C++ ,
- 更多C/C++疑问解答:
- 关于c++的cout输出的问题。
- 在学校里学过C和C++,不过学的很一般,现在自学C#,会不会很难?
- 全国计算机二级C语言笔试题
- 已知某树有2个2度结点,3个3度结点,4个4度结点,问有几个叶子结点?
- c++数据结构内部排序问题,整数排序
- 2012九月计算机二级C语言全国题库,,急求急求
- 如果assert只有一个字符串作为参数,是什么意思呢?
- C语言中,哪些运算符具有左结合性,哪些具有右结合性,帮忙总结下,谢谢了!
- 为什么用结构体编写的程序输入是,0输不出来啊~~~
- 将IEEE—754的十六进制转化为十进制浮点类型,用C或C++都行,多谢各位大侠啊,非常感谢!
- 为什么这个程序求不出公式?
- 这个链表倒置的算法请大家分析下
- c语言函数库调用
- C语言unsigned int纠错
- C语言快排求解啊