您的位置:首页 > 其它

蓝桥杯地铁换乘问题

2013-04-11 21:11 183 查看
为解决交通难题,某城市修建了若干条交错的地铁线路,线路名及其所属站名如stations.txt所示。

线1

苹果园

....

四惠东

线2

西直门

车公庄

....

建国门

线4

....

其中第一行数据为地铁线名,接下来是该线的站名。

当遇到空行时,本线路站名结束。

下一行开始又是一条新线....直到数据结束。

如果多条线拥有同一个站名,表明:这些线间可以在该站换车。

为引导旅客合理利用线路资源,解决交通瓶颈问题,该城市制定了票价策略:

1. 每条线路可以单独购票,票价不等。

2. 允许购买某些两条可换乘的线路的联票。联票价格低于分别购票。

单线票价和联合票价如 price.txt 所示。

线1 180

.....

线13 114

线1,线2 350

线1,线10 390

.....

每行数据表示一种票价

线名与票价间用空格分开。如果是联票,线名间用逗号分开。

联票只能包含两条可换乘的线路。

现在的问题是:根据这些已知的数据,计算从A站到B站最小花费和可行的换乘方案。

比如,对于本题目给出的示例数据

如果用户输入:

五棵松,奥体中心

程序应该输出:

-(线1,线10)-线8 = 565

如果用户输入:

五棵松,霍营

程序应该输出:

-线1-(线4,线13) = 440

可以看出,用户输入的数据是:起始站,终到站,用逗号分开。

程序输出了购票方案,在括号中的表示联票,短横线(-)用来分开乘车次序。

等号后输出的是该方案的花费数值。

分析:

自顶向下求解。先根据需求定义求解流程(顺序),再根据每个流程定义数据结构,函数(包括功能,参数),最后实现每个函数。对于线路信息的数据结构定义,因为每个线路的站名均为汉字,且有不少重复站名,并且汉字间比较更费时间,所以无论从空间还是时间考虑都应将其映射到其它数据结构,这是将站名与整数建立映射关系,每个站名都与一个整数对应,可以使用C++中map完成此功能或自已用HASH表实现,这里使用效率低些但实现简单的字符串指针数组实现,价格的数据结构实现与此类似。对于结果的计算有很多情况需要处理,如:

1. 当某个线路可直达时,是否还查找该线路可转乘到达的线路(以下程序查找);

2. 当同一联票线路有多种转乘方案时,该联票线路输出几回(以下程序有几种方案就输出几回);

3. 当有转乘线路可到达但无联票价格时是否不输出该方案(以下程序不输出)。

WIN32控制台程序不能输入中文汉字解决办法:

打开注册表(开始--运行--输入"regedit"回车),将"HKEY_CURRENT_USER--Console"中的"LoadConIme"修改为"1",然后在控制台中按"Ctrl+Space(空格)"可切换中文或英文输入。

解:

[cpp] view
plaincopy

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <io.h>

#include <ctype.h>

#define FALSE false

#define TRUE true

#define BOOL bool

typedef void VOID;

typedef int INT32;

typedef char INT8;

typedef long LONG;

#define MAX_LINES 50

#define LINE_STATIONS_NUM 100

#define ALL_STATIONS_NUM (MAX_LINES*LINE_STATIONS_NUM)

#define FILE_BUF_NUM 100

#define INPUT_BUF_NUM 50

#define OUPUT_BUF_NUM 100

#define MAX_RES_NUM 20

#define MAX_TWO_PRICE_NUM 100

//线

#define FLAG1 -49

#define FLAG2 -33

typedef struct LINE_ST

{

INT32 ln; //线号

INT32 num; //线内站数目

INT32 price; //线钱

INT32 ns[LINE_STATIONS_NUM]; //线内站号

}Line;

typedef struct TWO_PRICE_ST

{

INT32 num1; //线路号

INT32 num2; //线路号

INT32 price; //联票价格

}TwoPrice; //联票价格

/*****************************************************************************

* 函数:ReadLines *

* 参数:stLine:线路信息结构体. *

* pStaTable:站名(字符串)-序号(stLine.ns,stLine.cro)表. *

* szLinesFile:线路文件名. *

* 返回值:返回TRUE表示读取成功,FALSE失败. *

* 功能:读取线路信息. *

*****************************************************************************/

BOOL ReadLines(Line *stLine, INT8 *pStaTable[], INT8 *szLinesFile)

{

FILE *fp;

INT32 i,j;

INT32 n, ln; //站对应的数字,线号,

INT8 str[FILE_BUF_NUM]; //文件缓冲

n = 0;

ln = -1;

for(i=0; i<ALL_STATIONS_NUM; i++)

pStaTable[i] = NULL;

for(i=0; i<MAX_LINES; i++)

{

stLine[i].ln = -1;

stLine[i].price = 0;

stLine[i].num = 0;

for(j=0; j<LINE_STATIONS_NUM; j++)

{

stLine[i].ns[j] = 0;

}

}

if(NULL == (fp = fopen(szLinesFile, "r")))

{

printf("Err: Open stations.txt!\n");

return FALSE;

}

while(fgets(str, FILE_BUF_NUM, fp))

{

if('\n' == str[0])//空行?

continue;

// 去掉尾部不必要的换行符号

if(str[strlen(str)-1] == '\n' )

str[strlen(str)-1] = '\0';

if(FLAG1==str[0] && FLAG2==str[1]) //新线号?

{

ln++;

stLine[ln].ln = atoi(&str[2]);

continue;

}

for(i=0; i<n; i++)

{

if(0 == strcmp(str, pStaTable[i]))

break;

}

if(i==n)

{

pStaTable[i] = (INT8 *)malloc(sizeof(INT8)*LINE_STATIONS_NUM);

strcpy(pStaTable[i], str);

n++;

}

stLine[ln].ns[stLine[ln].num] = i;

stLine[ln].num++;

}

fclose(fp);

/*

for(i=0; NULL!=pStaTable[i]; i++)

{

printf("%d:%s\n", i, pStaTable[i]);

}

for(i=0; -1!=stLine[i].ln; i++)

{

printf("线%d: %d\n", stLine[i].ln, stLine[i].num);

for(j=0; j<stLine[i].num; j++)

{

printf("%d:%s\n", stLine[i].ns[j], pStaTable[stLine[i].ns[j]]);

}

}

*/

return TRUE;

}

/*****************************************************************************

* 函数:FreeReadLines *

* 参数:pStaTable:站名(字符串)-序号(stLine.ns,stLine.cro)表. *

* 返回值:无. *

* 功能:释放动态分配的pStaTable空间. *

*****************************************************************************/

VOID FreeReadLines(INT8 *pStaTable[])

{

INT32 i;

for(i=0; i<ALL_STATIONS_NUM; i++)

{

if(NULL != pStaTable[i])

free(pStaTable[i]);

}

}

/*****************************************************************************

* 函数:GetInput *

* 参数:szStart:起点站名. *

* szDes:终点站名. *

* 返回值:无. *

* 功能:读取用户输入的起点,终点. *

*****************************************************************************/

VOID GetInput(INT8 *szStart, INT8 *szDes) //获取用户输入

{

INT8 buf[INPUT_BUF_NUM];

INT32 i,j;

printf("请输入起始站和终点站(英文逗号隔开)......\n");

//英文逗号不能作为字符串分割符,即scanf("%s,%s", szStart, szDes)是错误的

fflush(stdin);

gets(buf);;

for(i=0; ','!=buf[i]; i++)

szStart[i] = buf[i];

szStart[i++] = '\0';

for(j=0; '\0'!=buf[i]; i++,j++)

szDes[j] = buf[i];

szDes[j] = '\0';

// printf("%s,%s\n", szStart, szDes);

}

/*****************************************************************************

* 函数:ReadLines *

* 参数:stLine:线路信息结构体. *

* pStaTable:站名(字符串)-序号(stLine.ns,stLine.cro)表. *

* szStart:起点站名. *

* szDes:终点站名. *

* aRes:结果,aRes[i][0]为线路1在stLine中序号,aRes[i][1]为线路2(若有) *

* 在stLine中序号. *

* 返回值:返回TRUE表示计算路线成功,FALSE失败. *

* 功能:计算起点到终点的可行乘车方案. *

*****************************************************************************/

BOOL CalcLines(Line *stLine, INT8 *pStaTable[], INT8 *szStart, INT8 *szDes,

INT32 aRes[][2]) //计算结果

{

INT32 i,j,k,l,m,n;

INT32 nStart = -1, nDes = -1; //起点,终点-序号(stLine.ns,stLine.cro)表

BOOL bStartFlag = FALSE, bDesFlag = FALSE; //找到起点,终点标志

INT32 nNotFind; //未找到的(起点或终点)

INT32 nResCount = 0; //结果计数

INT32 nLoop; //循环次数

aRes[0][0] = -1;

aRes[0][1] = -1;

//查找起点,终点在stLine中序号

for(i=0; NULL!=pStaTable[i]; i++)

{

if(0 == strcmp(szStart, pStaTable[i]))

nStart = i;

if(0 == strcmp(szDes, pStaTable[i]))

nDes = i;

}

if(-1==nStart || -1==nDes)

return FALSE;

//查找保存结果

for(i=0; -1!=stLine[i].ln; i++)

{

bStartFlag = FALSE;

bDesFlag = FALSE;

for(k=0; k<stLine[i].num; k++)

{

if(nStart == stLine[i].ns[k])

bStartFlag = TRUE;

if(nDes == stLine[i].ns[k])

bDesFlag = TRUE;

}

if(!bStartFlag && !bDesFlag) //未找到起点和终点?

continue;

nLoop = 1;

if(bStartFlag && bDesFlag) //找到起点和终点?

{

aRes[nResCount][0] = i;

aRes[nResCount][1] = -1;//?

nResCount++;

aRes[nResCount][0] = -1;

aRes[nResCount][1] = -1;

nLoop = 2; //以此线为起点或终点寻找可换乘路线,虽然一般没人这么做...

}

for(l=0; l<nLoop; l++)

{

if(1 == l)

bStartFlag = !bStartFlag;

if(bStartFlag) //只找到起点?

nNotFind = nDes;

else//只找到终点

nNotFind = nStart;

//找是否存在转乘线路可到达

for(j=i+1; -1!=stLine[j].ln; j++)

{

for(k=0; k<stLine[j].num; k++)

{

if(nNotFind == stLine[j].ns[k])

break;

}

if(k != stLine[j].num) //找到另一点

{

for(m=0; m<stLine[i].num; m++)

for(n=0; n<stLine[j].num; n++)

{

if(stLine[i].ns[m] == stLine[j].ns

&& nStart!=stLine[i].ns[m] && nDes!=stLine[i].ns[m]

&& nStart!=stLine[j].ns
&& nDes!=stLine[j].ns
) //转乘可到达?

{

aRes[nResCount][0] = i;

aRes[nResCount][1] = j;

nResCount++;

aRes[nResCount][0] = -1;

aRes[nResCount][1] = -1;

}

}

}

else

{

continue;

}

}

}

}

// for(i=0; -1!=aRes[i][0]; i++)

// printf("Line1:%d, Line2:%d\n", aRes[i][0], aRes[i][1]);

return TRUE;

}

/*****************************************************************************

* 函数:ReadLinesPrice *

* 参数:stLine:线路信息结构体. *

* sttwoPrice:联票价格. *

* szPriceFile:票价文件名. *

* 返回值:返回TRUE表示读取文件成功,FALSE失败. *

* 功能:读取票价. *

*****************************************************************************/

BOOL ReadLinesPrice(Line *stLine, TwoPrice *sttwoPrice,

INT8 *szPriceFile)//获取线路价格

{

FILE *fp;

INT8 c;

INT32 line1, line2;

INT32 price;

INT32 i, n = 0;

sttwoPrice[0].num1 = -1; //结束标志

if(NULL == (fp=fopen(szPriceFile, "r")))

{

printf("Err: Do not open %s\n", szPriceFile);

return FALSE;

}

while((FLAG1 == (c=fgetc(fp))) && EOF!=c)

{

// fPos = ftell(fp);

if((FLAG2 != (c=fgetc(fp))))

{

// if(0 != fseek(fp, fPos, SEEK_SET)) //文件指针向前移动1字节(返回判断该值是否等于FLAG1)

// {

// printf("Err: seek file err...\n");

// return FALSE;

// }

if(EOF != c) //文件指针向前移动1字节(返回判断该值是否等于FLAG1)

ungetc(c, fp);

else

{

fclose(fp);

return TRUE;

}

continue;

}

////找到“线”字,读完这一行在返回

//读线号

line1 = 0;

while((' ' != (c=fgetc(fp))) && ','!=c)

{

line1 = line1 * 10 + c - '0';

}

price = 0;

if(' ' == c) //此行为单线价格?

{

while('\n' != (c=getc(fp)))

price = price * 10 + c - '0';

//记录单线价格

for(i=0; i<MAX_LINES; i++)

{

if(line1 == stLine[i].ln)

{

stLine[i].price = price;

break;

}

}

}

else //联票价格行

{

while(FLAG1 != (c=getc(fp)));

c = getc(fp); //FLAG2

//读第二个线号

line2 = 0;

while(' ' != (c=getc(fp)))

line2 = line2*10 + c - '0';

while('\n' != (c=getc(fp)) && EOF != c)

{

if(isdigit(c))

price = price * 10 + c- '0';

}

//记录联票价格

sttwoPrice
.num1 = line1;

sttwoPrice
.num2 = line2;

sttwoPrice
.price = price;

n++;

sttwoPrice
.num1 = -1;

}

}

fclose(fp);

// for(i=0; -1!=stLine[i].ln; i++)

// printf("Line:%d Price:%d\n", stLine[i].ln, stLine[i].price);

// for(i=0; -1!=sttwoPrice[i].num1; i++)

// printf("Line1:%d Line2:%d Price:%d\n", sttwoPrice[i].num1,

// sttwoPrice[i].num2, sttwoPrice[i].price);

return TRUE;

}

/*****************************************************************************

* 函数:OutputRes *

* 参数:aRes:结果. *

* stLine:线路信息结构体. *

* sttwoPrice:联票价格. *

* 返回值:无. *

* 功能:输出结果. *

*****************************************************************************/

VOID OutputRes(INT32 aRes[][2], Line *stLine, TwoPrice *sttwoPrice) //输出结果

{

INT32 i, j;

INT32 aPrice[MAX_RES_NUM];

INT32 nMinNum = -1; //最小价格序号(aRes中)

INT32 nMinPrice = ~(1<<(sizeof(INT32)*8 - 1)) ; //最小价格

for(i=0; i<MAX_RES_NUM; i++)

aPrice[i] = -1;

//找出最便宜的结果

for(i=0; -1!=aRes[i][0]; i++)

{

if(-1 == aRes[i][1]) //单线?

{

aPrice[i] = stLine[aRes[i][0]].price;

if(stLine[aRes[i][0]].price < nMinPrice)

{

nMinPrice = stLine[aRes[i][0]].price;

nMinNum = i;

}

}

else //双线

{

for(j=0; -1!=sttwoPrice[j].num1; j++)

{

if((stLine[aRes[i][0]].ln==sttwoPrice[j].num1 && stLine[aRes[i][1]].ln==sttwoPrice[j].num2)

|| (stLine[aRes[i][0]].ln==sttwoPrice[j].num2 && stLine[aRes[i][1]].ln==sttwoPrice[j].num1))

{

aPrice[i] = sttwoPrice[j].price;

if(sttwoPrice[j].price < nMinPrice)

{

nMinPrice = sttwoPrice[j].price;

nMinNum = i;

}

}

}

}

}

//输出

for(i=0; -1!=aRes[i][0]; i++)

{

if(i != nMinNum && -1 != aPrice[i])

{

if(-1 == aRes[i][1]) //单线

{

printf("-线%d", stLine[aRes[i][0]].ln);

}

else//双线

{

printf("-(线%d,线%d)", stLine[aRes[i][0]].ln, stLine[aRes[i][1]].ln);

}

}

}

if(-1 != nMinNum)

{

if(-1==aRes[nMinNum][1])

printf("-线%d=%d\n", stLine[aRes[nMinNum][0]].ln, nMinPrice);

else

printf("-(线%d,线%d)=%d\n", stLine[aRes[nMinNum][0]].ln, stLine[aRes[nMinNum][1]].ln, nMinPrice);

}

}

BOOL f()

{

INT32 aRes[MAX_RES_NUM][2]; //结果(aRes[i][],i为stLine中序号)

INT8 *pStaTable[ALL_STATIONS_NUM]; //站名(字符串)-序号(stLine.ns,stLine.cro)表

INT8 szStart[INPUT_BUF_NUM], szDes[INPUT_BUF_NUM]; //起点,终点

INT8 *szLinesFile = "stations.txt";

INT8 *szPriceFile = "price.txt";

Line stLine[MAX_LINES]; //线

TwoPrice stTwoPrice[MAX_TWO_PRICE_NUM]; //2个线路的合票价格

if(!ReadLines(stLine, pStaTable, szLinesFile)) //获取线路信息

return FALSE;

if(!ReadLinesPrice(stLine, stTwoPrice, szPriceFile)) //获取线路价格

return FALSE;

GetInput(szStart, szDes); //获取用户输入

CalcLines(stLine, pStaTable, szStart, szDes, aRes); //计算结果

OutputRes(aRes, stLine, stTwoPrice); //输出结果

FreeReadLines(pStaTable); //释放已分配空间

return TRUE;

}

INT32 main(INT32 argc, INT32 *argv[])

{

f();

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: