zoukankan      html  css  js  c++  java
  • 【刷题笔记】火车购票-----java方案

    问题描述
    请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配。
      假设一节车厢有20排、每一排5个座位。为方便起见,我们用1到100来给所有的座位编号,第一排是1到5号,第二排是6到10号,依次类推,第20排是96到100号。
      购票时,一个人可能购一张或多张票,最多不超过5张。如果这几张票可以安排在同一排编号相邻的座位,则应该安排在编号最小的相邻座位。否则应该安排在编号最小的几个空座位中(不考虑是否相邻)。
      假设初始时车票全部未被购买,现在给了一些购票指令,请你处理这些指令。
    样例说明
    输入格式
      输入的第一行包含一个整数n,表示购票指令的数量。
      第二行包含n个整数,每个整数p在1到5之间,表示要购入的票数,相邻的两个数之间使用一个空格分隔。
    输出格式
      输出n行,每行对应一条指令的处理结果。
      对于购票指令p,输出p张车票的编号,按从小到大排序。
    样例输入
    4
    2 5 4 2
    样例输出
    1 2 1) 购2张票,得到座位1、2。
    6 7 8 9 10 2) 购5张票,得到座位6至10。
    11 12 13 14 3) 购4张票,得到座位11至14。
    3 4 4) 购2张票,得到座位3、4。    
      
    ---------------------------------------------------------------请思考的分割线----------------------------------------------------------------------

      读者看到这里的时候,想必心里已经有自己的想法了。无论可行性如何,有想法才是最重要的。接下来我们来看一下这个问题的解决思路吧。(个人能力有限,所以贡献的算法也难免局限,如果大家有更好的想法,欢迎讨论)

      首先,看到问题的时候第一思路是建立一个数组来记录100个座位的占用情况,对于每一次的购票行为,只需要对数组进行遍历,找到满足要求的位置并占用即可。当然这种方法的缺陷也很明显,每一次的购票行为都需要对数组进行遍历,而且由于每一排有5个座位的限制,所以在遍历的时候仍然要考虑连续的座位是否在同一排中,这无疑会增加算法的复杂性。

      进一步想,既然题目的座位是20排,那我们可以直接选择二维数组作为我们记录占用情况的数据结构。针对二维数组而言,我们只需要按行遍历,对于每一行判断是否可以容纳本次的购票人数。这种方法的遍历情况跟上一个方法是一样的,优势在于每一次的子数组长度都是5,不需要另行判断。

      上面的方法都局限在同一个问题,即对座位数组的反复遍历。尤其是对于那些已经占用的座位,每一次的遍历都是白白地消耗时间,如果,(敲黑板,重点来了)对于已经被占用的位置,我们可以减少访问,那么我们的运行时间就可以缩短!!!

      针对上面的两种思路我们来进行改进,我们先从一维数组开始考虑(因为二维数组的想留给读者自己试验)。在购票过程中,我们会尽量地把同一批客户安排在连续的同一排座位。那么我们在每次安排座位的时候,在被占用的数组中标示连续占用的座位数,这样的话,在我们遍历的时候就可以直接利用这个数字跳过这段连续的区域,减少访问次数。

      看完上面这段,读者可能对方法还是很疑惑。语言描述实在是不够直观,我们不妨看一下示意图。

              

      如果读者看图已经明白了原理,不妨先去实现一下。如果还是觉得抽象,我们就来细细地看图说话吧。

      上图显示的就是示例中的购票过程,首先将数组seats初始化为0,表示座位空置。

      第一次购票2张,占用位置(数组片段)是1、2,把seats[1]的数字置为2,表示由此开始的连续两个座位被占用。等到遍历数组时,在这个位置可以对下标进行+2操作,直接跳过这一段被占用的座位。

      第二次购票是5张,从头开始遍历数组,访问第一个位置,被占用且数字为2,则跳转到1+2=3号位置,3号座位为0表示空置,但是3--5之间只有三个空位,不能容纳5个人,所以跳转到下一排,即6号位置,6号位满足要求,在此处落座并更改seats[6] = 5。

      第三次购票是4张,依然从头开始,1号位非0,跳转至1+2=3号位;3号位为0,但是空间不足,跳转至6号位;6号位非0,跳转至6+5=11号位;11号位为0且空间足够,落座并更改seats[11] = 4。

      第四次购票是2张,遍历过程如上,1号位非0,转至1+2=3号位;3号位为0且空间足够,落座并更改seats[3]=2。

      到此为止,例程部分的购票已经结束。如果某次购票行为中发现没有足够的连续空位,就要选择散座,即重新从头开始遍历,并且每次只坐一个人。方法同上。

      总结一下,在这个版本的算法中,我们合理利用了开辟的数组,记录了被连续占用的段落长度,从而减少了遍历的次数,好像已经前进了一步。但是这个算法仍然可能需要进行两次遍历,即第一次找不到连续座位然后重新找散座。因为题目中的要求是先尽量连续分配,失败则分配散座,所以两次遍历操作是难免的。

      考虑到以上的情况,再来想一想能不能在遍历过程中再精简一些。

    ---------------------------------------------------------------请思考的分割线----------------------------------------------------------------------

      

      仔细分析一下,上面的过程中对于每一排座位,是在发现空位之后才去判断空位数是否足够。从二维数组的角度考虑一下,如果每一行的第一个位置标示了这一排已经坐下的人数x,那么我们就可以很直接的通过5-x得到空余的座位。这样的话我们就不必要先跳转到空位,而可以直接判断是否在这一排落座,如果选择落座的话,则利用排首坐标+x跳转至座位。
      相比之下,二维数组的情况跟一维是相似的,甚至更简单,我们在每一行的第一个位置记录下本行已经被占用的座位数,这样的话每次只需要访问seats[*][1]的数字,就可以得知该行是否可以坐并且可以直接找到空位,落座的时候也只需要让seats[*][1]加上本次坐下的人数。

    PS:读者如果注意到上面seats[*][1]里的*,应该明白其实完全可以只用一个20位的数组解决。哇塞,进步好大啊!!问题不难,读者请动手试试吧。(注意,文中的数组起始下标是1,请根据实际情况修正)

     PPS:俗话说得好,talk is cheap,show me the code。下面贴上对于一维数组的相对比较复杂的算法实现。大家试着实现其它的吧。

     1 Scanner fin = new Scanner(System.in);      //有一些语句是为了对应提交题目的格式,读者自动选择有价值的部分参观
     2 int N = fin.nextInt();
     3         
     4 int [] seat = new int[100];
     5         
     6 for(int i=0;i<N;i++){
     7         int num = fin.nextInt();
     8         int pos = 0;
     9         while(pos < 100){
    10             if(seat[pos] == 0){
    11                 if(pos%5 + num <= 5) break;
    12                 pos = pos + 5 - pos%5;
    13             }else{
    14                 pos += seat[pos];
    15             }
    16         }
    17         if(pos < 100){
    18             seat[pos] = num;
    19             while(num > 0){
    20                 String cc = Integer.toString(++pos);
    21                 System.out.print(cc + " ");
    22                 num --;
    23             }
    24         }else{
    25             pos = 0;
    26             while(num > 0){
    27                 if (seat[pos] == 0){
    28                     String cc = Integer.toString(pos+1);
    29                     System.out.print(cc + " ");
    30                     seat[pos++] = 1;
    31                     num--;
    32                 }else{
    33                     pos += seat[pos];    
    34                 }
    35             }
    36         }
    37         System.out.println("");
    38  }
  • 相关阅读:
    R语言:提取路径中的文件名字符串(basename函数)
    课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)—— 0、学习目标
    numpy.squeeze()的用法
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 4、Logistic Regression with a Neural Network mindset
    Python numpy 中 keepdims 的含义
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 3、Python Basics with numpy (optional)
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 2、编程作业常见问题与答案(Programming Assignment FAQ)
    课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 0、学习目标
    课程一(Neural Networks and Deep Learning),第一周(Introduction to Deep Learning)—— 0、学习目标
    windows系统numpy的下载与安装教程
  • 原文地址:https://www.cnblogs.com/GuoYaxiang/p/6052777.html
Copyright © 2011-2022 走看看