前言

电梯月来咯~

虽然本单元理论上应该写多线程,但是我却采用了状态机逻辑…虽然这可能与训练目标不太一样,不过是正确的就好辣~

整体结构

经过仔细地评估、设计后,我的代码中除了 MainClass 外只包含四个类。类图如下所示:

类图

  • Request:请求
  • Building:楼
  • Elevator:电梯
  • Passenger:乘客

对各类的分析与评估

Request

本类占据一个线程,负责拉取乘客的请求。

评价:没啥好说的。

Building

本类占据一个线程,负责六部电梯的运行以及分配乘客请求给电梯。

方法介绍:

step

使状态机前进一个周期。

isEnd

判断状态机整体是否运行完毕。

elevatorEnd

判断各个电梯是否运行完毕。

pasAlloc

尝试将乘客分配给电梯,分配失败返回 false

findNearEmpty

寻找与目标楼层最近的空电梯。

Building评价

本类是状态机的核心类。有关状态机的逻辑都包含在本类之中。

Elevator

电梯类。负责电梯的具体行为。

对于电梯,我具有两个乘客队列:tasks 队列中的乘客未入梯但是已被分配给这个电梯;passengers 队列中的乘客则已经进入了这个电梯。

在判断能否接客时,采用一个桶子(即属性 cnt)记载电梯的占用情况。

方法介绍:

tryOpen

尝试开门操作。

operator

尝试进行关门、开门操作。

move

尝试进行移动操作。

ifCarry

判断电梯是否可以接受该乘客。(主要从容量方面考虑该乘客是否能买到“票”)

ifPassingly

判断电梯是否可以捎带该乘客。

Elevator评价

本类是电梯类,承载了电梯的所有行为逻辑。

Passenger

本类是乘客类,记录了一个乘客所包含的所有信息。

对于乘客的 state(状态)定义如下:

  • 0:乘客尚未进入电梯
  • 1:乘客已经进入电梯

没啥好评价的,普普通通的乘客类。

运行逻辑

电梯的忙碌状态定义:

  • free:电梯中没有乘客且任务队列为空。
  • empty:电梯中没有乘客但任务队列非空。
  • busy:电梯中存在乘客。

电梯的运行状态(action)定义:

  • true:本轮时钟周期可以进行行动。
  • false:本轮时钟周期已经行动过了,不能进行行动。

Building 在初始化时会创建六个电梯。

一个时钟周期设置为 400ms

每轮时钟周期首先尝试给电梯分配任务。分配完任务后电梯的运行逻辑如下:

1
2
3
4
5
6
7
8
for (Elevator elevator: elevators) {
if (!elevator.isFree() || elevator.isOpen()) {
elevator.init(); // 将 action 置为 true
elevator.operator();
elevator.move();
elevator.tryOpen(); // 到了一层后可以尝试开门
}
}

始终步进直至达到停止条件。

移动策略

首先需要保证每时每刻都保证电梯的乘客队列和任务队列中所有请求的方向(下文简称为请求方向)是一致的(由调度保证)。

电梯在运行共分为三个状态:

FREE:保持不动

EMPTY:前往最底部的请求所处的楼层(如果请求方向是上则要尽可能去更低的楼层开始接人,反之同理)。

BUSY:保持当前运行方向直至所有乘客都到达了目的地。

乘客分配策略

首先尝试将乘客分配给电梯捎带。

这里的捎带分为两种情况:

  • 对于 empty 的电梯:
    • 乘客与电梯运行方向一致时:待分配的乘客与电梯当前运行方向相同,且电梯尚未运行到乘客起始楼层,且电梯容量足够,则判断为可捎带。
    • 乘客与电梯运行方向不一致时:待分配的乘客与电梯任务队列中的乘客的请求方向一致,且电梯容量足够,则判断为可捎带。
  • 对于 busy 的电梯:待分配的乘客与电梯当前运行方向相同,且电梯尚未运行到乘客起始楼层,且电梯容量足够,则判断为可捎带。

若无法捎带,则分配给最近的空电梯。这里的空电梯指电梯处于 free 状态,即乘客队列与任务队列均为空。

复杂度分析

method CogC ev(G) iv(G) v(G)
Building.Building(int, ArrayList\, Request) 1.0 1.0 2.0 2.0
Building.elevatorEnd() 3.0 3.0 2.0 3.0
Building.findNearEmpty(int) 6.0 1.0 4.0 4.0
Building.isEnd() 1.0 1.0 2.0 2.0
Building.pasAlloc(Passenger) 4.0 4.0 4.0 4.0
Building.run() 3.0 2.0 2.0 3.0
Building.step() 7.0 1.0 6.0 6.0
Elevator.carry(Passenger) 6.0 1.0 1.0 4.0
Elevator.checkBegin() 5.0 2.0 4.0 6.0
Elevator.checkTop() 2.0 1.0 1.0 3.0
Elevator.close() 0.0 1.0 1.0 1.0
Elevator.closeDoor() 3.0 1.0 3.0 3.0
Elevator.Elevator(int, int, int, int, int) 1.0 1.0 1.0 2.0
Elevator.findBottom() 3.0 1.0 3.0 3.0
Elevator.findTop() 3.0 1.0 3.0 3.0
Elevator.getFloor() 0.0 1.0 1.0 1.0
Elevator.ifCarry(Passenger) 12.0 6.0 1.0 6.0
Elevator.ifOut() 3.0 3.0 2.0 3.0
Elevator.ifOut(Passenger) 1.0 1.0 2.0 2.0
Elevator.ifPassingly(Passenger) 6.0 5.0 3.0 5.0
Elevator.ifTake() 3.0 3.0 2.0 3.0
Elevator.ifTake(Passenger) 1.0 1.0 3.0 3.0
Elevator.init() 0.0 1.0 1.0 1.0
Elevator.isEmpty() 0.0 1.0 1.0 1.0
Elevator.isEnd() 1.0 1.0 2.0 2.0
Elevator.isFree() 1.0 1.0 2.0 2.0
Elevator.isOpen() 0.0 1.0 1.0 1.0
Elevator.move() 13.0 2.0 4.0 7.0
Elevator.open() 0.0 1.0 1.0 1.0
Elevator.operator() 7.0 2.0 5.0 6.0
Elevator.takeOn() 3.0 1.0 3.0 3.0
Elevator.takeOn(Passenger) 0.0 1.0 1.0 1.0
Elevator.takeOut() 3.0 1.0 3.0 3.0
Elevator.takeOut(Passenger) 0.0 1.0 1.0 1.0
Elevator.tryOpen() 3.0 2.0 2.0 4.0
MainClass.main(String[]) 0.0 1.0 1.0 1.0
Passenger.getDirection() 0.0 1.0 1.0 1.0
Passenger.getFinish() 0.0 1.0 1.0 1.0
Passenger.getId() 0.0 1.0 1.0 1.0
Passenger.getStart() 0.0 1.0 1.0 1.0
Passenger.isTake() 0.0 1.0 1.0 1.0
Passenger.Passenger(int, int, int) 1.0 1.0 1.0 2.0
Passenger.takeOn() 0.0 1.0 1.0 1.0
Passenger.toString() 0.0 1.0 1.0 1.0
Request.getRequest() 4.0 3.0 3.0 3.0
Request.isEnd() 0.0 1.0 1.0 1.0
Request.Request(ArrayList\) 0.0 1.0 1.0 1.0
Request.run() 1.0 1.0 1.0 2.0
Total 111.0 73.0 95.0 122.0
Average 2.3125 1.5208333333333333 1.9791666666666667 2.5416666666666665

本次作业类之间关系比较简单,总体上有高内聚,低耦合的特点。

Bug分析

本次作业出了一个很离谱的 bug。我竟然忘记在下客人的时候维护桶子了。太悲伤了……

最后寄了三个点,都是 tle。因为这个 bug 会极大的影响我的性能,但是不会影响正确性。(最后就会变成只有空电梯才能接客,而不能捎带客人,从而 tle 掉。)