蓝桥杯 程序设计_8

为解决交通难题,某城市修建了若干条交错的地铁线路,线路名及其所属站名如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(空格)"可切换中文或英文输入。

解:

#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[n]
								&& nStart!=stLine[i].ns[m] && nDes!=stLine[i].ns[m]
								&& nStart!=stLine[j].ns[n] && nDes!=stLine[j].ns[n])	//转乘可到达?
							{
								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[n].num1 = line1;
			sttwoPrice[n].num2 = line2;
			sttwoPrice[n].price = price;
			n++;
			sttwoPrice[n].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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值