蓝桥杯地铁换乘问题

为解决交通难题,某城市修建了若干条交错的地铁线路,线路名及其所属站名如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 plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include <io.h>  
  5. #include <ctype.h>  
  6.   
  7. #define FALSE   false  
  8. #define TRUE    true  
  9. #define BOOL    bool  
  10.   
  11. typedef void    VOID;  
  12. typedef int     INT32;  
  13. typedef char    INT8;  
  14. typedef long    LONG;  
  15.   
  16. #define MAX_LINES           50  
  17. #define LINE_STATIONS_NUM   100  
  18. #define ALL_STATIONS_NUM    (MAX_LINES*LINE_STATIONS_NUM)  
  19. #define FILE_BUF_NUM        100  
  20. #define INPUT_BUF_NUM       50  
  21. #define OUPUT_BUF_NUM       100  
  22. #define MAX_RES_NUM         20  
  23. #define MAX_TWO_PRICE_NUM   100  
  24. //线  
  25. #define FLAG1               -49  
  26. #define FLAG2               -33  
  27.   
  28. typedef struct LINE_ST  
  29. {  
  30.     INT32 ln;       //线号  
  31.     INT32 num;      //线内站数目  
  32.     INT32 price;    //线钱  
  33.     INT32 ns[LINE_STATIONS_NUM];    //线内站号  
  34. }Line;  
  35.   
  36. typedef struct TWO_PRICE_ST  
  37. {  
  38.     INT32 num1;     //线路号  
  39.     INT32 num2;     //线路号  
  40.     INT32 price;    //联票价格  
  41. }TwoPrice;          //联票价格  
  42.   
  43. /***************************************************************************** 
  44. * 函数:ReadLines                                                           * 
  45. * 参数:stLine:线路信息结构体.                                                 * 
  46. *      pStaTable:站名(字符串)-序号(stLine.ns,stLine.cro)表.                  * 
  47. *      szLinesFile:线路文件名.                                                * 
  48. * 返回值:返回TRUE表示读取成功,FALSE失败.                                  * 
  49. * 功能:读取线路信息.                                                         * 
  50. *****************************************************************************/  
  51. BOOL ReadLines(Line *stLine, INT8 *pStaTable[], INT8 *szLinesFile)  
  52. {  
  53.     FILE *fp;  
  54.     INT32 i,j;  
  55.     INT32 n, ln;            //站对应的数字,线号,  
  56.     INT8 str[FILE_BUF_NUM]; //文件缓冲  
  57.   
  58.     n = 0;  
  59.     ln = -1;  
  60.   
  61.     for(i=0; i<ALL_STATIONS_NUM; i++)  
  62.         pStaTable[i] = NULL;  
  63.   
  64.     for(i=0; i<MAX_LINES; i++)  
  65.     {  
  66.         stLine[i].ln = -1;  
  67.         stLine[i].price = 0;  
  68.         stLine[i].num = 0;  
  69.         for(j=0; j<LINE_STATIONS_NUM; j++)  
  70.         {  
  71.             stLine[i].ns[j] = 0;  
  72.         }  
  73.     }  
  74.       
  75.   
  76.     if(NULL == (fp = fopen(szLinesFile, "r")))  
  77.     {  
  78.         printf("Err: Open stations.txt!\n");  
  79.         return FALSE;  
  80.     }  
  81.   
  82.     while(fgets(str, FILE_BUF_NUM, fp))  
  83.     {     
  84.         if('\n' == str[0])//空行?  
  85.             continue;  
  86.   
  87.         // 去掉尾部不必要的换行符号  
  88.         if(str[strlen(str)-1] == '\n' )  
  89.             str[strlen(str)-1] = '\0';  
  90.   
  91.         if(FLAG1==str[0] && FLAG2==str[1])  //新线号?  
  92.         {  
  93.             ln++;  
  94.             stLine[ln].ln = atoi(&str[2]);  
  95.             continue;  
  96.         }  
  97.           
  98.         for(i=0; i<n; i++)  
  99.         {  
  100.             if(0 == strcmp(str, pStaTable[i]))  
  101.                 break;  
  102.         }  
  103.   
  104.         if(i==n)  
  105.         {  
  106.             pStaTable[i] = (INT8 *)malloc(sizeof(INT8)*LINE_STATIONS_NUM);  
  107.             strcpy(pStaTable[i], str);  
  108.             n++;  
  109.         }  
  110.   
  111.         stLine[ln].ns[stLine[ln].num] = i;  
  112.         stLine[ln].num++;  
  113.   
  114.     }  
  115.   
  116.     fclose(fp);  
  117.   
  118. /* 
  119.     for(i=0; NULL!=pStaTable[i]; i++) 
  120.     { 
  121.         printf("%d:%s\n", i, pStaTable[i]); 
  122.     } 
  123.  
  124.     for(i=0; -1!=stLine[i].ln; i++) 
  125.     { 
  126.         printf("线%d: %d\n", stLine[i].ln, stLine[i].num); 
  127.         for(j=0; j<stLine[i].num; j++) 
  128.         { 
  129.             printf("%d:%s\n", stLine[i].ns[j], pStaTable[stLine[i].ns[j]]); 
  130.         } 
  131.     } 
  132. */  
  133.   
  134.     return TRUE;  
  135. }  
  136.   
  137. /***************************************************************************** 
  138. * 函数:FreeReadLines                                                       * 
  139. * 参数:pStaTable:站名(字符串)-序号(stLine.ns,stLine.cro)表.                    * 
  140. * 返回值:无.                                                                 * 
  141. * 功能:释放动态分配的pStaTable空间.                                             * 
  142. *****************************************************************************/  
  143. VOID FreeReadLines(INT8 *pStaTable[])  
  144. {  
  145.     INT32 i;  
  146.   
  147.     for(i=0; i<ALL_STATIONS_NUM; i++)  
  148.     {  
  149.         if(NULL != pStaTable[i])  
  150.             free(pStaTable[i]);  
  151.     }  
  152. }  
  153.   
  154. /***************************************************************************** 
  155. * 函数:GetInput                                                            * 
  156. * 参数:szStart:起点站名.                                                   * 
  157. *      szDes:终点站名.                                                       * 
  158. * 返回值:无.                                                                 * 
  159. * 功能:读取用户输入的起点,终点.                                           * 
  160. *****************************************************************************/  
  161. VOID GetInput(INT8 *szStart, INT8 *szDes)   //获取用户输入  
  162. {  
  163.     INT8 buf[INPUT_BUF_NUM];  
  164.     INT32 i,j;  
  165.     printf("请输入起始站和终点站(英文逗号隔开)......\n");  
  166.     //英文逗号不能作为字符串分割符,即scanf("%s,%s", szStart, szDes)是错误的  
  167.     fflush(stdin);  
  168.     gets(buf);;  
  169.     for(i=0; ','!=buf[i]; i++)  
  170.         szStart[i] = buf[i];  
  171.     szStart[i++] = '\0';  
  172.     for(j=0; '\0'!=buf[i]; i++,j++)  
  173.         szDes[j] = buf[i];  
  174.     szDes[j] = '\0';  
  175.   
  176. //  printf("%s,%s\n", szStart, szDes);  
  177. }  
  178.   
  179. /***************************************************************************** 
  180. * 函数:ReadLines                                                           * 
  181. * 参数:stLine:线路信息结构体.                                                 * 
  182. *      pStaTable:站名(字符串)-序号(stLine.ns,stLine.cro)表.                  * 
  183. *      szStart:起点站名.                                                     * 
  184. *      szDes:终点站名.                                                       * 
  185. *      aRes:结果,aRes[i][0]为线路1在stLine中序号,aRes[i][1]为线路2(若有) * 
  186. *           在stLine中序号.                                                  *     
  187. * 返回值:返回TRUE表示计算路线成功,FALSE失败.                                * 
  188. * 功能:计算起点到终点的可行乘车方案.                                         * 
  189. *****************************************************************************/  
  190. BOOL CalcLines(Line *stLine, INT8 *pStaTable[], INT8 *szStart, INT8 *szDes,   
  191.                INT32 aRes[][2]) //计算结果  
  192. {  
  193.     INT32 i,j,k,l,m,n;  
  194.     INT32 nStart = -1, nDes = -1;   //起点,终点-序号(stLine.ns,stLine.cro)表  
  195.     BOOL  bStartFlag = FALSE, bDesFlag = FALSE; //找到起点,终点标志  
  196.     INT32 nNotFind;                     //未找到的(起点或终点)  
  197.     INT32 nResCount = 0;                //结果计数  
  198.     INT32 nLoop;                        //循环次数  
  199.   
  200.     aRes[0][0] = -1;  
  201.     aRes[0][1] = -1;  
  202.   
  203.     //查找起点,终点在stLine中序号  
  204.     for(i=0; NULL!=pStaTable[i]; i++)  
  205.     {  
  206.         if(0 == strcmp(szStart, pStaTable[i]))  
  207.             nStart = i;  
  208.         if(0 == strcmp(szDes, pStaTable[i]))  
  209.             nDes = i;  
  210.     }  
  211.     if(-1==nStart || -1==nDes)  
  212.         return FALSE;  
  213.   
  214.     //查找保存结果  
  215.     for(i=0; -1!=stLine[i].ln; i++)  
  216.     {  
  217.         bStartFlag = FALSE;  
  218.         bDesFlag = FALSE;  
  219.         for(k=0; k<stLine[i].num; k++)  
  220.         {  
  221.             if(nStart == stLine[i].ns[k])  
  222.                 bStartFlag = TRUE;  
  223.             if(nDes == stLine[i].ns[k])  
  224.                 bDesFlag = TRUE;  
  225.         }  
  226.   
  227.         if(!bStartFlag && !bDesFlag)    //未找到起点和终点?  
  228.             continue;  
  229.   
  230.         nLoop = 1;  
  231.         if(bStartFlag && bDesFlag)  //找到起点和终点?  
  232.         {  
  233.             aRes[nResCount][0] = i;  
  234.             aRes[nResCount][1] = -1;//?  
  235.             nResCount++;  
  236.             aRes[nResCount][0] = -1;  
  237.             aRes[nResCount][1] = -1;  
  238.             nLoop = 2;      //以此线为起点或终点寻找可换乘路线,虽然一般没人这么做...  
  239.         }  
  240.           
  241.         for(l=0; l<nLoop; l++)  
  242.         {  
  243.             if(1 == l)  
  244.                 bStartFlag = !bStartFlag;  
  245.   
  246.             if(bStartFlag)  //只找到起点?  
  247.                 nNotFind = nDes;  
  248.             else//只找到终点  
  249.                 nNotFind = nStart;  
  250.   
  251.             //找是否存在转乘线路可到达  
  252.             for(j=i+1; -1!=stLine[j].ln; j++)  
  253.             {  
  254.                 for(k=0; k<stLine[j].num; k++)  
  255.                 {  
  256.                     if(nNotFind == stLine[j].ns[k])  
  257.                         break;  
  258.                 }  
  259.                 if(k != stLine[j].num)  //找到另一点  
  260.                 {  
  261.                     for(m=0; m<stLine[i].num; m++)  
  262.                         for(n=0; n<stLine[j].num; n++)  
  263.                         {  
  264.                             if(stLine[i].ns[m] == stLine[j].ns[n]  
  265.                                 && nStart!=stLine[i].ns[m] && nDes!=stLine[i].ns[m]  
  266.                                 && nStart!=stLine[j].ns[n] && nDes!=stLine[j].ns[n])    //转乘可到达?  
  267.                             {  
  268.                                 aRes[nResCount][0] = i;  
  269.                                 aRes[nResCount][1] = j;  
  270.                                 nResCount++;  
  271.                                 aRes[nResCount][0] = -1;  
  272.                                 aRes[nResCount][1] = -1;  
  273.                             }  
  274.                         }  
  275.                 }  
  276.                 else  
  277.                 {  
  278.                     continue;  
  279.                 }  
  280.             }  
  281.         }  
  282.           
  283.     }  
  284.   
  285. //  for(i=0; -1!=aRes[i][0]; i++)  
  286. //      printf("Line1:%d, Line2:%d\n", aRes[i][0], aRes[i][1]);  
  287.   
  288.     return TRUE;  
  289. }  
  290.   
  291. /***************************************************************************** 
  292. * 函数:ReadLinesPrice                                                          * 
  293. * 参数:stLine:线路信息结构体.                                                 * 
  294. *      sttwoPrice:联票价格.                                                  * 
  295. *      szPriceFile:票价文件名.                                                *    
  296. * 返回值:返回TRUE表示读取文件成功,FALSE失败.                                * 
  297. * 功能:读取票价.                                                           * 
  298. *****************************************************************************/  
  299. BOOL ReadLinesPrice(Line *stLine, TwoPrice *sttwoPrice,  
  300.                     INT8 *szPriceFile)//获取线路价格  
  301. {  
  302.     FILE *fp;  
  303.     INT8 c;  
  304.     INT32 line1, line2;  
  305.     INT32 price;  
  306.     INT32 i, n = 0;  
  307.   
  308.     sttwoPrice[0].num1 = -1;    //结束标志  
  309.   
  310.     if(NULL == (fp=fopen(szPriceFile, "r")))  
  311.     {  
  312.         printf("Err: Do not open %s\n", szPriceFile);  
  313.         return FALSE;  
  314.     }  
  315.   
  316.     while((FLAG1 == (c=fgetc(fp))) && EOF!=c)  
  317.     {  
  318. //      fPos = ftell(fp);  
  319.         if((FLAG2 != (c=fgetc(fp))))  
  320.         {  
  321. //          if(0 != fseek(fp, fPos, SEEK_SET))  //文件指针向前移动1字节(返回判断该值是否等于FLAG1)  
  322. //          {  
  323. //              printf("Err: seek file err...\n");  
  324. //              return FALSE;  
  325. //          }  
  326.             if(EOF != c)    //文件指针向前移动1字节(返回判断该值是否等于FLAG1)  
  327.                 ungetc(c, fp);  
  328.             else  
  329.             {  
  330.                 fclose(fp);  
  331.                 return TRUE;  
  332.             }  
  333.             continue;  
  334.         }  
  335.   
  336.         找到“线”字,读完这一行在返回  
  337.   
  338.         //读线号  
  339.         line1 = 0;  
  340.         while((' ' != (c=fgetc(fp))) && ','!=c)   
  341.         {  
  342.             line1 = line1 * 10 + c - '0';   
  343.         }  
  344.   
  345.         price = 0;  
  346.         if(' ' == c)    //此行为单线价格?  
  347.         {  
  348.             while('\n' != (c=getc(fp)))  
  349.                 price = price * 10 + c - '0';  
  350.   
  351.             //记录单线价格  
  352.             for(i=0; i<MAX_LINES; i++)     
  353.             {  
  354.                 if(line1 == stLine[i].ln)  
  355.                 {  
  356.                     stLine[i].price = price;  
  357.                     break;  
  358.                 }  
  359.             }  
  360.         }  
  361.         else    //联票价格行  
  362.         {  
  363.             while(FLAG1 != (c=getc(fp)));  
  364.             c = getc(fp);   //FLAG2  
  365.   
  366.             //读第二个线号  
  367.             line2 = 0;  
  368.             while(' ' != (c=getc(fp)))  
  369.                 line2 = line2*10 + c - '0';   
  370.   
  371.             while('\n' != (c=getc(fp)) && EOF != c)  
  372.             {  
  373.                 if(isdigit(c))  
  374.                     price = price * 10 + c- '0';  
  375.             }  
  376.   
  377.             //记录联票价格  
  378.             sttwoPrice[n].num1 = line1;  
  379.             sttwoPrice[n].num2 = line2;  
  380.             sttwoPrice[n].price = price;  
  381.             n++;  
  382.             sttwoPrice[n].num1 = -1;  
  383.         }  
  384.   
  385.     }  
  386.   
  387.     fclose(fp);  
  388.   
  389. //  for(i=0; -1!=stLine[i].ln; i++)  
  390. //      printf("Line:%d Price:%d\n", stLine[i].ln, stLine[i].price);  
  391. //  for(i=0; -1!=sttwoPrice[i].num1; i++)  
  392. //      printf("Line1:%d Line2:%d Price:%d\n", sttwoPrice[i].num1,   
  393. //              sttwoPrice[i].num2, sttwoPrice[i].price);  
  394.   
  395.     return TRUE;  
  396. }  
  397.   
  398. /***************************************************************************** 
  399. * 函数:OutputRes                                                           * 
  400. * 参数:aRes:结果.                                                            *    
  401. *      stLine:线路信息结构体.                                               * 
  402. *      sttwoPrice:联票价格.                                                  * 
  403. * 返回值:无.                                                                 * 
  404. * 功能:输出结果.                                                           * 
  405. *****************************************************************************/  
  406. VOID OutputRes(INT32 aRes[][2], Line *stLine, TwoPrice *sttwoPrice) //输出结果  
  407. {  
  408.     INT32 i, j;  
  409.     INT32 aPrice[MAX_RES_NUM];  
  410.     INT32 nMinNum = -1; //最小价格序号(aRes中)  
  411.     INT32 nMinPrice  = ~(1<<(sizeof(INT32)*8 - 1))  ;         //最小价格  
  412.   
  413.     for(i=0; i<MAX_RES_NUM; i++)  
  414.         aPrice[i] = -1;  
  415.   
  416.     //找出最便宜的结果  
  417.     for(i=0; -1!=aRes[i][0]; i++)  
  418.     {  
  419.         if(-1 == aRes[i][1])    //单线?  
  420.         {  
  421.             aPrice[i] = stLine[aRes[i][0]].price;  
  422.             if(stLine[aRes[i][0]].price < nMinPrice)  
  423.             {  
  424.                 nMinPrice = stLine[aRes[i][0]].price;  
  425.                 nMinNum = i;                  
  426.             }  
  427.         }  
  428.         else    //双线  
  429.         {  
  430.             for(j=0; -1!=sttwoPrice[j].num1; j++)  
  431.             {  
  432.                 if((stLine[aRes[i][0]].ln==sttwoPrice[j].num1 && stLine[aRes[i][1]].ln==sttwoPrice[j].num2)  
  433.                     || (stLine[aRes[i][0]].ln==sttwoPrice[j].num2 && stLine[aRes[i][1]].ln==sttwoPrice[j].num1))  
  434.                 {  
  435.                     aPrice[i] = sttwoPrice[j].price;  
  436.                     if(sttwoPrice[j].price < nMinPrice)  
  437.                     {  
  438.                         nMinPrice = sttwoPrice[j].price;  
  439.                         nMinNum = i;  
  440.                     }  
  441.                 }  
  442.   
  443.             }  
  444.         }  
  445.     }  
  446.   
  447.     //输出  
  448.     for(i=0; -1!=aRes[i][0]; i++)  
  449.     {  
  450.         if(i != nMinNum && -1 != aPrice[i])  
  451.         {  
  452.             if(-1 == aRes[i][1])    //单线  
  453.             {  
  454.                 printf("-线%d", stLine[aRes[i][0]].ln);  
  455.             }  
  456.             else//双线  
  457.             {  
  458.                 printf("-(线%d,线%d)", stLine[aRes[i][0]].ln, stLine[aRes[i][1]].ln);  
  459.             }  
  460.         }  
  461.     }  
  462.     if(-1 != nMinNum)  
  463.     {  
  464.         if(-1==aRes[nMinNum][1])  
  465.             printf("-线%d=%d\n", stLine[aRes[nMinNum][0]].ln, nMinPrice);  
  466.         else  
  467.             printf("-(线%d,线%d)=%d\n", stLine[aRes[nMinNum][0]].ln, stLine[aRes[nMinNum][1]].ln, nMinPrice);  
  468.     }  
  469. }  
  470.   
  471. BOOL f()  
  472. {  
  473.     INT32   aRes[MAX_RES_NUM][2];                           //结果(aRes[i][],i为stLine中序号)  
  474.     INT8    *pStaTable[ALL_STATIONS_NUM];                   //站名(字符串)-序号(stLine.ns,stLine.cro)表  
  475.     INT8    szStart[INPUT_BUF_NUM], szDes[INPUT_BUF_NUM];   //起点,终点  
  476.     INT8    *szLinesFile = "stations.txt";  
  477.     INT8    *szPriceFile = "price.txt";   
  478.     Line    stLine[MAX_LINES];                              //线  
  479.     TwoPrice    stTwoPrice[MAX_TWO_PRICE_NUM];              //2个线路的合票价格  
  480.   
  481.     if(!ReadLines(stLine, pStaTable, szLinesFile))          //获取线路信息  
  482.         return FALSE;  
  483.   
  484.     if(!ReadLinesPrice(stLine, stTwoPrice, szPriceFile))    //获取线路价格  
  485.         return FALSE;  
  486.   
  487.     GetInput(szStart, szDes);                               //获取用户输入  
  488.   
  489.     CalcLines(stLine, pStaTable, szStart, szDes, aRes);     //计算结果  
  490.   
  491.     OutputRes(aRes, stLine, stTwoPrice);                    //输出结果  
  492.   
  493.     FreeReadLines(pStaTable);                               //释放已分配空间  
  494.   
  495.     return TRUE;  
  496. }  
  497.   
  498. INT32 main(INT32 argc, INT32 *argv[])  
  499. {  
  500.   
  501.     f();  
  502.   
  503.     return 0;  
  504. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值