【刷题笔记】火车购票-----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。下面贴上对于一维数组的相对比较复杂的算法实现。大家试着实现其它的吧。
Scanner fin = new Scanner(System.in); //有一些语句是为了对应提交题目的格式,读者自动选择有价值的部分参观
int N = fin.nextInt(); int [] seat = new int[100]; for(int i=0;i<N;i++){
int num = fin.nextInt();
int pos = 0;
while(pos < 100){
if(seat[pos] == 0){
if(pos%5 + num <= 5) break;
pos = pos + 5 - pos%5;
}else{
pos += seat[pos];
}
}
if(pos < 100){
seat[pos] = num;
while(num > 0){
String cc = Integer.toString(++pos);
System.out.print(cc + " ");
num --;
}
}else{
pos = 0;
while(num > 0){
if (seat[pos] == 0){
String cc = Integer.toString(pos+1);
System.out.print(cc + " ");
seat[pos++] = 1;
num--;
}else{
pos += seat[pos];
}
}
}
System.out.println("");
}
【刷题笔记】火车购票-----java方案的更多相关文章
- 《Data Structures and Algorithm Analysis in C》学习与刷题笔记
<Data Structures and Algorithm Analysis in C>学习与刷题笔记 为什么要学习DSAAC? 某个月黑风高的夜晚,下班的我走在黯淡无光.冷清无人的冲之 ...
- Python 刷题笔记
Python 刷题笔记 本文记录了我在使用python刷题的时候遇到的知识点. 目录 Python 刷题笔记 选择.填空题 基本输入输出 sys.stdin 与input 运行脚本时传入参数 Pyth ...
- PTA刷题笔记
PTA刷题记录 仓库地址: https://github.com/Haorical/Code/tree/master/PTA/GPLT 两周之内刷完GPLT L2和L3的题,持续更新,包括AK代码,坑 ...
- PAT-甲级刷题笔记和总结
本帖主要记录一些自己在刷题过程中的一些笔记,包括: 1.常用的函数 2.STL中常用方法 3.常见错误 4.其他常用方法 5.刷题过程中的常见算法:https://www.cnblogs.com/M ...
- CCF201609-2 火车购票 java(100分)
试题编号: 201609-2 试题名称: 火车购票 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 请实现一个铁路购票系统的简单座位分配算法,来处理一节车厢的座位分配. 假设一 ...
- LeetCode刷题笔记 - 12. 整数转罗马数字
学好算法很重要,然后要学好算法,大量的练习是必不可少的,LeetCode是我经常去的一个刷题网站,上面的题目非常详细,各个标签的题目都有,可以整体练习,本公众号后续会带大家做一做上面的算法题. 官方链 ...
- 《剑指offer》刷题笔记
简介 此笔记为我在 leetcode 上的<剑指offer>专题刷题时的笔记整理. 在刷题时我尝试了 leetcode 上热门题解中的多种方法,这些不同方法的实现都列在了笔记中. leet ...
- 18.9.10 LeetCode刷题笔记
本人算法还是比较菜的,因此大部分在刷基础题,高手勿喷 选择Python进行刷题,因为坑少,所以不太想用CPP: 1.买股票的最佳时期2 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. ...
- 剑指offer刷题笔记
删除链表中重复的结点:较难 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4- ...
随机推荐
- js作用域
一.js没有块级作用域 在c,java等语言中花括号里的代码都有自己的作用域,而js花括号没有块级作用域,经常会导致一些困惑,不明所以.例如: console.info(color); if(true ...
- GFF3格式文件
GFF3是GFF注释文件的新标准.文件中每一行为基因组的一个属性,分为9列,以TAB分开. 依次是: 1. reference sequence:参照序列 指出注释的对象.如一个染色体,克隆或片段.可 ...
- VBA中使用计时器的两种方法
'================================ ' VBA采用Application.OnTime实现计时器 ' ' http://www.cnhup.com '========= ...
- Windows 10(64位)配置Caffe运行环境的基本流程
最新博客地址已搬家到: http://blog.csdn.net/zzlyw/article/details/53215148
- Spring框架学习(二)
一.Spring IOC参数值注入 1.注入基本值 1.1)<value></value>元素可以通过字符串指定属性或构造参数的值.容器将字符串从java.lang.Strin ...
- python之路二十
一, $.ajax,这个是JQuery对ajax封装的最基础步,通过使用这个函数可以完成异步通讯的所有功能.也就是说什么情况下我们都可以通过此方法进行异步刷新的操作.但是它的参数较多,有的时候可能会麻 ...
- matlab绘图基础
matlab绘制条形图并分组显示: a =[1 2 3] b =[4 5 6] >> d=[a;b] d = 1 2 3 4 5 6 >> bar(d,'group') 修改横 ...
- Tomcat的设定
tomcat 版本 apache-tomcat-7.0.68-windows-x64 1.解压文件到 eclipse文件夹中,这个放哪都可以,个人习惯而已 2.tomcat目录结构 图片为盗图- 3 ...
- linux du和df
df.du和fdisk这三个常用命令:df用于检查文件系统磁盘占用情况,du检查磁盘空间占用情况,而fdisk用于磁盘分区. du,disk usage,是通过搜索文件来计算每个文件的大小然后累加,d ...
- 【webGl】threejs实现一个简单的动画-弹跳的小球
在这里,我们将动态画面简称为动画(animation).正如动画片的原理一样,动画的本质是利用了人眼的视觉暂留特性,快速地变换画面,从而产生物体在运动的假象.而对于Three.js程序而言,动画的实现 ...