Github仓库地址:hua-kui
寒假学习计划:学习计划
- 题目背景
一栋10层的大楼(楼层编号1-10),设有一台无限载重的电梯,初始时电梯停在1层。电梯移动1层的耗时为1,在某一层停靠的耗时为1(时间初始为0)。为了使得乘客等待的时间(电梯在目的层的停靠时刻 - 乘客发出请求时刻)总和最小,请你编写一个程序来进行电梯调度。
输入有5个请求,每个请求一行,格式为请求时刻 起始楼层数 去往方向,其中方向为0代表向上去往10层,为1代表向下去往1层。
输出每次对应的决策,每一行的输出格式为xx时,停靠在x楼。其中,“xx时刻”指的是在某层楼停靠的时刻,且不算入在该层的停靠时间。如:
- 当0时刻时,电梯此时在1层,输入有0 1 0,那么电梯从1层接客(1s)前往10层(9s),应输出10时,停靠在10楼(1+9=10)。此时,该乘客等待时间为(10-0=)10。
- 当0时刻,电梯此时在1层,输入有0 2 0,那么电梯从1层前往2层(1s),接上乘客(1s),前往10层(8s),应输出10时,停靠在10楼(1+1+8=10)。此时,该乘客等待时间为(10-0=)10s。
最后输出完成5个请求(所有乘客都到达目的地)后,各乘客的等待时间总和。
ANS 1
- 代码情况
代码长度 | bug | 耗时 |
---|---|---|
175行 | 13处 | 12~14h |
- 分析
EMMM先看这个题目虽然觉得能随时变换方向还不消耗时间的电梯有点魔幻hhh(关注点不要在这里啦!)
好了看到没有时间的限制和其他的要求,我脑海里第一个跳出的就是暴搜(毕竟暴力出奇迹OTL)那么再来看数据点,经过简单分析,很明显,排除了某些213乘客会走楼梯的情况,那么大多数乘客只会在原地等待,因此,停靠在别的楼层是很显然没有意义的,如此一来,电梯只会在我们请求的地点(5个)+1楼+10楼停靠,当然,5个地点中也有可能会重复……
好啦接下来看数据构造w
- 数据模型
- 关于乘客
struct{
int time;//记录这名乘客发出请求的时刻
int ceng;//记录这名乘客发出请求的层数
int flag;//记录这名乘客去的方向 0表示向下,1表示向上
int zhuang;//记录这名乘客的状态 0表示等待进入,1表示在电梯中,2表示已经下电梯
int value;//记录这名乘客的到达时间:到达的时间-发出请求的time
}r[1004];
这个结构体是乘客的信息记录,其中value是我第一次算时间的时候太213了(其实是太弱)构造出来的,结果写起来非常麻烦也没有必要,想了好久,后来发现其实只要算电梯当前时间-乘客请求时间=等待时间就可以了,也就是在1楼和10楼进行统计
- 关于电梯
struct {
int time;//记录当前的时间
int floor;//电梯当前所在的楼层
int flag;//记录现在取得方向,有0 10两种
}dt;
这个东西实在DFS中记录实时情况的,对他进行更新,也就是所等待的时间即
abs(dt.time-r[i].time)再+1S+1S+1S(简直续命游戏)
这样后面的+1S可以在每次停靠的时候模拟处理+1S
- 数据更新
- 关于更新写了3个处理,也就是在1楼、10楼、和其他请求楼层
10楼
if(n==10)
for(int i=1;i<=5;i++)
if(r[i].flag==1&&r[i].zhuang==1)
{
r[i].zhuang=2;
}//如果当前是第10层,让电梯中需要出去的人出去
而1楼、和其他楼层依次类推,只需要让需要下车的人下车,需要上的人更新就行,需要注意的一点是我之前没有考虑到的,在之后回溯的时候,需要还原,而就需要开个数组记录这些下车或上车的人,把他们再还原丢出去。
- 最终目标
- 我们搜到的最终目标也就是让所有人的状态都变成2,也就是到达目的地为止,这样所有的请求都已经应答
- 实现
- 如此一来我们就可以根据上面的几个构造直接暴搜DFS遍历最优解,也就是第K次决策,停靠在……最后取最少时间
void dfs(int k){//第K次决策
int ji=0;
for(int i=1;i<=7;i++)//一个时刻有7种可能可以停靠,因为只会停靠在有人请求的楼层
{
if(i!=6&&i!=7&&r[i].zhuang==0)//如果不是1、10楼且还有未应答的人员
{
ji=dt.floor;//记录楼层
dt.time+=abs(dt.floor-r[i].ceng);
dt.floor=r[i].ceng;//模拟电梯移动
ans[k].time=dt.time;
ans[k].floor=dt.floor;//数据更新
for(int j=1;j<=5;j++)
if(r[j].ceng==r[i].ceng&&r[j].zhuang==0)
{
r[j].zhuang=1;
ask[j]=1;//如果当前停靠的层数有人要上,那就让他进入电梯
}
if(pd()==1)
pd2(t);//进行判断 :是否到达目标节点
else
dfs(k+1);//搜索下一策略
dt.time-=abs(dt.floor-r[i].ceng);
dt.floor=ji; //回溯回复
for(int j=1;j<=5;j++)
if(ask[j]==1)
{
r[j].zhuang=0;
}
for(int j=1;j<=5;j++)
ask[j]=0;//回溯回复,也就是上述的开一个记录,把进入的乘客丢出去
}
}
这是普通楼层的处理方式,而1、10楼的方式类似,但需要判断的地方更多,如:回溯时的复原等等(一开始就是这些小点全没有考虑到,然后……、死循了)
- 辛酸的心路历程(BUG处理)
爱之初体验
之前第一次没认真看的时候觉得还行,就是模拟,后来发现细节有一点多……(哭的像个两百斤的狗子、、特别是+1S的续命游戏一开始没有加上去)
一开始写的时候是定义成为第K时刻的决策,后来又更改成为第K层是否停留,把1-10层全遍历一边
-
爱之再选择
- 最终排除了刚开始的想法,推倒重新来,更改了核心语句,新开设了一个电梯的信息的记录组,用来统计实时状态,变为第K次决策,也就是第K步应该向哪里走。
- 细节的初始化和回溯……开了比较多的东西记录,一些边界还有初始化的修改出了比较大的问题,反复都在改这个
- 上面有提到的那个统计时间一开始纠结了好久,没有想到怎么去更新把时间弄上去,所以才会开了一个VALUE来记录,后来发现不用,因为电梯的信息也是要记录的,就减一下就OK
- 0 和 1也就是1和10写反了……找了好久
-
爱之最终题
- 为了更新电梯的实时情况,还写了一个调用函数,即
void gx(int n)//用来更新电梯中人员的状态,n表示当前层数
{
if(n==10)
for(int i=1;i<=5;i++)
if(r[i].flag==1&&r[i].zhuang==1)
{
r[i].zhuang=2;
}//如果当前是第10层,让电梯中需要出去的人出去
if(n==1)
for(int i=1;i<=5;i++)
if(r[i].flag==0&&r[i].zhuang==1)
{
r[i].zhuang=2;
}//如果当前是第1层,让电梯中需要出去的人出去
for(int i=1;i<=5;i++)
if(r[i].ceng==n&&r[i].zhuang==0)
r[i].zhuang=1;//如果当前停靠的层数有人要上,那就让他进入电梯
}
也就是当电梯门开了之后进行更新,后来发现调用这个函数的时候可能会对回溯恢复带来比加大的麻烦,因此就更改了写法,不用上述的函数。
- 写完了之后发现出了上述的BUG与思考,还存在一些情况,那就是当一个比较久后的时间,比如前4个数据都是在1-20秒内做出请求,而第5个请求是在100秒时,当前4名乘客已经下站,做为一架未卜先知的电梯,应当做出他的选择,即:预先到所在地点等候。这又是一种特殊的情况因此添加了进去。
- 当然还存在一些情况,例如当第10层会不会有人发出请求,出了又进……(虽然这是很213的行为好吧)
- PS:还会不断优化 ,在处理某些数据的时候还可能会有未知BUG等
- 数据构造
- 第一类 顶层和底层
0 10 1 1 1 0
0 10 1 1 10 1
0 10 1 1 1 0
0 1 0 1 10 1
0 1 0 1 10 1
符合数据要求
- 第二类 正常小数据
0 4 0 0 1 0
0 5 0 1 10 1
0 6 0 2 1 0
0 7 0 3 5 1
0 8 0 4 6 1
符合数据要求
- 第三类 随机大数据时刻
4565 4 1 8742 1 0
4343 5 1 2345 9 1
5533 9 1 2323 1 0
1232 9 0 3565 5 1
3232 8 0 3323 6 1
符合数据要求
- 第四类 请求时刻大于运行时间
0 4 1 99 1 0
0 5 1 1 9 1
0 9 1 2 1 0
0 9 0 5 5 1
99 8 0 11 6 1
出现BUG:分析,超过运行时间回溯时出现错误
- 第五类 随机数据(出现重复楼层)
0 7 1 99 9 0
66 5 1 1 9 1
0 8 1 2 9 0
0 6 0 5 5 1
99 8 0 11 6 1
出现BUG:分析,第一个 99 9 0数据超出运行时间,回溯出现错误
ANS 2
做为不是一个未卜先知的电梯(虽然ANS 1中提前知道5个请求有点细思恐极),只需要按照5个请求顺推模拟即可,需要考虑的细节也是挺多的emmm
ANS 3
现实生活中的电梯:在群上看到有同学讨论现实生活中的两部并列电梯的运作方式(显然不会未卜先知和突然掉头),还挺有兴趣的,有想法的同学可以在下面评论留言或者私聊一起讨论呀~
Pintia小作业