zoukankan      html  css  js  c++  java
  • 【9006】最优乘车

    Time Limit: 1 second
    Memory Limit: 256 MB

    问题描述
    H城是一个旅游胜地,每年都有成千上万的人前来观光。为方便游客,巴士公司在各个旅游景点及宾馆,饭店等地都设置了巴士站并
    开通了一些单程巴上线路。每条单程巴士线路从某个巴士站出发,依次途经若干个巴士站,最终到达终点巴士站。
    一名旅客最近到H城旅游,他很想去S公园游玩,但如果从他所在的饭店没有一路已士
    可以直接到达S公园,则他可能要先乘某一路巴士坐几站,再下来换乘同一站台的另一路巴士, 这样换乘几次后到达S公园。
    现在用整数1,2,…N 给H城的所有的巴士站编号,约定这名旅客所在饭店的巴士站编号为1…S公园巴士站的编号为N。
    写一个程序,帮助这名旅客寻找一个最优乘车方案,使他在从饭店乘车到S公园的过程
    中换车的次数最少。  

    Input

    文件的第一行有两个数字M和N(1<=M<=100 1<n<=500),表
     示开通了M条单程巴士线路,总共有N个车站。从第二行到第M刊行依次给出了第1条到
    第M条巴士线路的信息。其中第i+1行给出的是第i条巴士线路的信息,从左至右按运行顺序依次给出了该线路上的所有站号相邻两个
    站号之间用一个空格隔开。

    Output

    如果无法乘巴士从饭店到达S公园,则输出
    "N0",否则输出你的程序所找到的最少换车次数,换车次数为0表示不需换车即可到达· 

    Sample Input

    3 7
    
    6 7
    
    4 7 3 6
    
    2 1 3 5
    
    
    
    
    

    Sample Output

    2

    【题解】(两种方法)

    法一:

    用dis[i][j]表示到第i条线路的j站台(如果存在j站台,比如样例的一号线,没有1,2,3,4,5站台)的最少换车数。

    找到每一个线路的一号站台,用作起点,假设循环变量为i,dis[i][1] = 0;然后把这个点加入队列中。进行广搜。每次分两类进行搜索。

    第一类。在这个起点所在的线路进行搜索。尝试更新这一线路的某个站台的最优解。如果更新了,那么这个解一定是最优的。加入队列,或者是答案直接输出。

    同一路线的花费为0;

    第二类。去找其他线路的同一站台。换到那一个线路的同一位置。这时花费为1。记录下已经换到另外一条线路即可.

    【代码1】

    <pre name="code" class="cpp">#include <cstdio>
    #include <iostream>
    #include <string>
    #include <stdlib.h>
    
    using namespace std;
    
    int m,n,a[101][599],where[101][599],dis[101][599],team[10000][2]; //a数组用来记录线路信息,
    //wher[i][j]表示第i线路的j号站台在i号线路的编号,dis[i][j]用于记录到第i条线路j号站台的最优解。
    //team为广搜时使用的队列。二维用来记录坐标信息。 
    
    void input_data()
    {
    	scanf("%d%d",&m,&n);
    	getchar();
    	for (int i = 1;i <= m;i++)
    		{
    			string ss;
    			getline(cin,ss); //整行读下来,根据空格来确定每一个位置的数字 
    			int p;
    			int j = 1;
    			while ( ( p = ss.find(" ",0)) != -1) //如果还能找到空格的话就继续操作获取数字 
    				{
    					string temp = ss.substr(0,p);
    					a[i][j] = atoi(ss.c_str());
    					where[i][a[i][j]] = j;
    					j++;
    					ss = ss.erase(0,p+1);
    				}
    			a[i][j] = atoi(ss.c_str()); //用atoi函数来把字符串转成数字 
    			where[i][a[i][j]] = j; //记录下这个站台在这个线路的编号 
    			a[i][0] = j; //记录下这个线路有几个站台 
    		}
    }
    
    void bfs()
    {
    	for (int i = 1;i <= m;i++)
    		for (int j = 1;j <= n;j++) //一开始的-1表示正无穷。 
    			dis[i][j] = -1;
    	int head = 1,tail =0;
    	for (int i = 1;i <= m;i++) //找到1号站台。置初始值。 
    		if (where[i][1]!=0)
    			{
    				tail++;	 //把1号站台作为起点加入队列中 
    				team[tail][0] = i;
    				team[tail][1] = where[i][1];
    				dis[i][1] = 0; //不用换车就直接到了 
    			}
    	while (head <= tail) //开始进行广搜 
    		{
    			int x0 = team[head][0],y0 = team[head][1]; //取出队列的第一个元素 
    			int what = a[x0][y0];	//what表示这个站台具体是1..n中的哪一个 
    			int step0 = dis[x0][what]; //获取已经换车的次数 
    			head++;
    			for (int i = y0 +1;i <= a[x0][0];i++) //在同一个线路当中搜索,尝试不花费,更新最优解 
    				if (dis[x0][a[x0][i]] == -1 || dis[x0][a[x0][i]] > step0)
    					{ //************特别提醒,如果数据中n==1,这里for的i的下界应该改为y0; 
    						dis[x0][a[x0][i]] = step0;
    						if (a[x0][i] == n) //如果找到最优解则直接输出答案 
    							{
    								printf("%d
    ",step0);
    								exit(0);	
    							}
    						tail++; //不是最优解则把这个点加入队列中 
    						team[tail][0] = x0;
    						team[tail][1] = i;
    					}
    			for (int i = 1;i <= m;i++)	//这是在其他线路当中寻找和当前位置的站台相同的站台 
    				if (i!=x0) //如果不是同一线路则可以换路线到那个线路去 
    					{
    						int kk = where[i][what]; //找到其在线路中的编号 
    						if (kk != 0 && (dis[i][what] == -1 || dis[i][what] > step0 +1) )
    							{ //如果对方为正无穷或者能够在消耗1次换车的情况下更新则更新 
    								tail++; //这种情况不可能为最优解。所以不用判断。 
    								team[tail][0] = i;
    								team[tail][1] = kk;
    								dis[i][what] = step0 + 1;
    							}
    					}
    				
    		}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	input_data();
    	bfs();
    	printf("NO"); //最后要记得输出无解信息。 
    	return 0;	
    }


    
    

    法二

    用最短路算法做。

    如两条线路

    1 3 5

    3 2 4 6

    在1-3 1-5 3-5

    3-2 3- 4 3-6 2-4 2-6 4-6

    这些点之间增加一条权值为1的边。当然边是有向的。

    然后再求从1到6的最短路。

    3-5可以理解为从一号线路换了一次到了2号线路的3,然后再不花费换车次数的情况下到达5,而1-5的过程多算了一次换车次数。最后减掉就好了。

    即输出w[1,n]-1;

    【代码2】

    #include <cstdio>
    
    int m,n,a[150][510],w[501][501];
    
    void input_data()
    {
    	scanf("%d%d",&m,&n);
    	for (int i =1; i <= m;i++)
    		{
    			int now = 0;
    			char t = 1;
    			while (t != '
    ') //如果还没有换行 那么久继续读。这是一个好的技巧。 
    				{
    					now++;
    					scanf("%d",&a[i][now]);
    					t = getchar(); //获取下一个字符(如果不是结尾的话就获取到了空格) 
    				}
    			for (int f = 1;f <= now-1;f++) //从f开始,为f和f之后的站台之间加上一条权值为1的边。 
    				for (int t = f+1;t <= now;t++)
    					w[a[i][f]][a[i][t]] = 1;
    		}
    	for (int i = 1;i <= n;i++)
    		for (int j = 1;j <= n;j++) //如果没有边则置为无穷大。 
    			if (w[i][j] == 0)
    				w[i][j] = 2100000000/3;
    }
    
    void get_ans()
    {
    	for (int k = 1;k <= n;k++) //用floyed算法求出任意两点的最短路 
    		for (int i = 1;i <= n;i++)
    			for (int j = 1;j <= n;j++)
    				if (w[i][j] > w[i][k] + w[k][j])
    					w[i][j] = w[i][k] + w[k][j];	
    }
    
    void output_ans()
    {
    	if (w[1][n] >= 2100000000/3) //如果1-n之间没有路线可以走,则输出无解信息 
    		{
    			printf("NO");
    			return;
    		}
    	printf("%d
    ",w[1][n] - 1); //否则输出答案。 
    }
    
    int main()
    {
    	//freopen("F:\rush.txt","r",stdin);
    	input_data();
    	get_ans();
    	output_ans();
    	return 0;	
    }




  • 相关阅读:
    SAP CRM One Order函数CRM_Object_FILL_OW的设计原理
    SAP CRM One Order函数CHANGE_OW的设计原理
    SAP CRM One Order函数SAVE_EC的设计原理
    POJ-1125 Stockbroker Grapevine
    GStreamer 1.0 series序列示例
    H265与ffmpeg改进开发
    FFmpeg扩展开发
    在Yolov5 Yolov4 Yolov3 TensorRT 实现Implementation
    TensorRT 基于Yolov3的开发
    大规模数据处理Apache Spark开发
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632406.html
Copyright © 2011-2022 走看看