Blog1:PTA题集5-7

电梯调度题目集5~7总结Blog
前言
我的Java学习经历

学了八周Java后,我感觉自己从啥也不会到能写点代码,进步还是有的。但题目集5到7的电梯调度问题真是让我头大,算是目前遇到的最大挑战。我花了好多时间改代码、查错,从一开始连编译都过不了,到后来能跑但答案不对,再到跑得太慢(超时)。虽然三次都没完全通过PTA的测试,但我没放弃,每次失败都让我学到新东西,比如怎么组织代码、处理复杂输入。这过程虽然累,但也让我对Java更熟悉了。

题目集概况
除了三个最后一题以外,其他题都还是挺简单的,花不了太多时间
剩下的这三个题目都是关于控制一部电梯的程序,一次次进行迭代设计

题目集5:写一个电梯程序,处理内部请求(乘客在电梯里按楼层)和外部请求(外面的人按上行/下行按钮)。电梯默认停在1楼,先处理同一方向的请求,处理完再处理反方向的,还要忽略无效楼层(比如楼层超范围)。这时候没有太多的类设计
题目集6:把代码拆成几个小块(类),每个类只干一件事,还要跳过重复的请求(比如连按三次3楼),增加了类的设计
题目集7:加个“乘客”类,外部请求从<楼层,方向>改成<起始楼层,目标楼层>,电梯接人后要把目标楼层加到内部请求里。

难度:

题目集5我个人认为最难,主要是搞清楚电梯的运行规律就很困难,很多边界情况我并不清楚。
题目集6只是在5的基础上迭代,感觉写起来更像是优化,而且有类的设计后会更加清晰。
题目集7也是进行优化,新的请求格式和目标楼层处理让我挺困扰的。
这三次让我学会了怎么把大的需求拆成小块、用列表管理请求、写更清楚的代码,但也暴露了我很多不足,下面会细说。

设计与分析
题目集5:第一个电梯程序
题目要求

点击查看代码
设计一个电梯类,具体包含电梯的最大楼层数、最小楼层数(默认为1层)当前楼层、运行方向、运行状态,以及电梯内部乘客的请求队列和电梯外部楼层乘客的请求队列,其中,电梯外部请求队列需要区分上行和下行。
电梯运行规则如下:电梯默认停留在1层,状态为静止,当有乘客对电梯发起请求时(各楼层电梯外部乘客按下上行或者下行按钮或者电梯内部乘客按下想要到达的楼层数字按钮),电梯开始移动,当电梯向某个方向移动时,优先处理同方向的请求,当同方向的请求均被处理完毕然后再处理相反方向的请求。电梯运行过程中的状态包括停止、移动中、开门、关门等状态。当电梯停止时,如果有新的请求,就根据请求的方向或位置决定移动方向。电梯在运行到某一楼层时,检查当前是否有请求(访问电梯内请求队列和电梯外请求队列),然后据此决定移动方向。每次移动一个楼层,检查是否有需要停靠的请求,如果有,则开门,处理该楼层的请求,然后关门继续移动。
使用键盘模拟输入乘客的请求,此时要注意处理无效请求情况,例如无效楼层请求,比如超过大楼的最高或最低楼层。还需要考虑电梯的空闲状态,当没有请求时,电梯停留在当前楼层。
请编写一个Java程序,设计一个电梯类,包含状态管理、请求队列管理以及调度算法,并使用一些测试用例,模拟不同的请求顺序,观察电梯的行为是否符合预期,比如是否优先处理同方向的请求,是否在移动过程中处理顺路的请求等。为了降低编程难度,不考虑同时有多个乘客请求同时发生的情况,即采用串行处理乘客的请求方式(电梯只按照规则响应请求队列中当前的乘客请求,响应结束后再响应下一个请求),具体运行规则详见输入输出样例。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:

运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例:
在这里给出一组输入。例如:

1
20
<3,UP>
<5>
<6,DOWN>
<7>
<3>
end
输出样例:
在这里给出相应的输出。例如:

Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

我要写一个Elevator类,包含:

最低和最高楼层(比如1到20)。

当前楼层(默认1楼)。

方向(上行UP、下行DOWN、停止IDLE)。

状态(停止STOPPED、移动MOVING)。

两种请求:
内部请求:电梯里的人按楼层(格式:<5>)。
外部请求:外面的人按上行或下行按钮。 电梯先处理同一方向的请求,处理完再换方向,到达有请求的楼层就开门关门,没请求时停在当前楼层。还得忽略无效楼层(比如21楼)。

我的代码
我写了一个Elevator类,把所有功能都塞进去:

变量:
minFloor和maxFloor:楼层范围。
currentFloor:当前楼层,从minFloor开始。
direction:用枚举(UP/DOWN/IDLE),比字符串清楚。
state:用枚举(STOPPED/MOVING)。
三个队列:internalRequests(内部请求)、externalUpRequests(外部上行)、externalDownRequests(外部下行),都用LinkedList。
主要函数:
addInternalRequest和addExternalRequest:把请求加到队列,检查楼层是否有效。
processRequests:主循环,处理所有请求直到队列为空。
determineDirection:根据队列里的第一个请求决定方向。
move:电梯移动一层,检查是否要停。
shouldStop:看当前楼层有没有请求需要处理。
processCurrentFloorRequests:清除当前楼层的请求。
核心代码片段:

点击查看代码
class Elevator {
    private final int minFloor;
    private final int maxFloor;
    private int currentFloor;
    private Direction direction;
    private State state;
    private Queue<Integer> internalRequests;
    private Queue<ExternalRequest> externalUpRequests;
    private Queue<ExternalRequest> externalDownRequests;

    enum Direction { UP, DOWN, IDLE }
    enum State { STOPPED, MOVING, OPENING, CLOSING }

    static class ExternalRequest {
        int floor;
        Direction direction;
        ExternalRequest(int floor, Direction direction) {
            this.floor = floor;
            this.direction = direction;
        }
    }

    public Elevator(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
        this.currentFloor = minFloor;
        this.state = State.STOPPED;
        this.direction = Direction.IDLE;
        this.internalRequests = new LinkedList<>();
        this.externalUpRequests = new LinkedList<>();
        this.externalDownRequests = new LinkedList<>();
    }

    public boolean addInternalRequest(int floor) {
        if (floor < minFloor || floor > maxFloor) {
            System.out.println("Invalid floor: " + floor);
            return false;
        }
        internalRequests.add(floor);
        return true;
    }

    public void processRequests() {
        while (!internalRequests.isEmpty() || !externalUpRequests.isEmpty() || !externalDownRequests.isEmpty()) {
            determineDirection();
            if (direction != Direction.IDLE) {
                move();
            }
        }
        direction = Direction.IDLE;
        state = State.STOPPED;
    }
}
**代码分析**

成果与问题
实现了:
简单输入(比如<3,UP>)能正确移动到3楼。
用LinkedList管理请求,添加和删除很方便。
用枚举让代码更清楚,比如Direction.UP比字符串"UP"好。
不足的:
所有功能都在Elevator类里,太乱了,找错很费劲。
没处理重复请求,比如<3,UP><3,UP>会让电梯在3楼停两次。
方向选择太简单,只看第一个请求,可能走远路。

题目集6:
题目要求

点击查看代码
对之前电梯调度程序进行迭代性设计,目的为解决电梯类职责过多的问题,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客请求类、队列类以及控制类,具体设计可参考如下类图。
电梯运行规则与前阶段单类设计相同,但要处理如下情况:

乘客请求楼层数有误,具体为高于最高楼层数或低于最低楼层数,处理方法:程序自动忽略此类输入,继续执行
乘客请求不合理,具体为输入时出现连续的相同请求,例如<3><3><3>或者<5,DOWN><5,DOWN>,处理方法:程序自动忽略相同的多余输入,继续执行,例如<3><3><3>过滤为<3>
注意:本次作业类设计必须符合如上要求(包含但不限于乘客请求类、电梯类、请求队列类及控制类,其中控制类专门负责电梯调度过程),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次电梯程序提交到本次题目中测试)。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<乘客所在楼层数,乘梯方向>,其中,乘梯方向用UP代表上行,用DOWN代表下行(UP、DOWN必须大写)。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:

运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例1:
在这里给出一组输入。例如:

1
20
<3,UP>
<5>
<6,DOWN>
<7>
<3>
end
输出样例1:
在这里给出相应的输出。例如:

Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
输入样例2:
在这里给出一组输入。例如:

1
20
<3,UP>
<3,UP>
<5>
<5>
<5>
<6,DOWN>
<7>
<7>
<3>
<22,DOWN>
<5,DOWN>
<30>
END
输出样例2:
在这里给出相应的输出。例如:

Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Open Door # Floor 3
Close Door
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Open Door # Floor 6
Close Door
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door

这次要把代码分成几个类,每个类只干一件事,还要跳过重复请求(比如<3><3>)和无效楼层。需要:

Elevator类:管楼层、方向、状态。
Request类:存请求信息。
RequestQueue类:管理所有请求。
ElevatorController类:控制电梯怎么跑。

我的代码
我用了四个类:

Elevator:只管楼层、方向、状态。
Request:存楼层和请求类型(内部、上行、 下行),加了比较功能去重。
RequestQueue:用LinkedHashSet存请求,自动去重。
ElevatorController:决定电梯移动和停靠。
核心代码(RequestQueue):

点击查看代码
class RequestQueue {
    private final Set<Request> internalRequests = new LinkedHashSet<>();
    private final Set<Request> externalUpRequests = new LinkedHashSet<>();
    private final Set<Request> externalDownRequests = new LinkedHashSet<>();
    private final int minFloor;
    private final int maxFloor;

    public RequestQueue(int minFloor, int maxFloor) {
        this.minFloor = minFloor;
        this.maxFloor = maxFloor;
    }

    public boolean addInternalRequest(int floor) {
        if (floor < minFloor || floor > maxFloor) return false;
        return internalRequests.add(new Request(floor, RequestType.INTERNAL));
    }

    public boolean addExternalUpRequest(int floor) {
        if (floor < minFloor || floor >ascapeX({% raw %}) return false;
    }
}
**代码分析**

成果与问题
实现了:
分成几个类后,代码清楚多了,每块功能分开。
用LinkedHashSet自动跳过重复请求,比如<3,UP><3,UP>。
无效楼层直接忽略,程序更稳。
未实现:
方向选择还是看第一个请求,可能走冤枉路。
有些测试没过,可能是没处理好同一楼层有上行和下行请求的情况。
大量请求时跑得慢,检查队列太费时间。
类图:

题目集7:
题目要求

点击查看代码
对之前电梯调度程序再次进行迭代性设计,加入乘客类(Passenger),取消乘客请求类,类设计要求遵循单一职责原则(SRP),要求必须包含但不限于设计电梯类、乘客类、队列类以及控制类,具体设计可参考如下类图。
电梯运行规则与前阶段相同,但有如下变动情况:

乘客请求输入变动情况:外部请求由之前的<请求楼层数,请求方向>修改为<请求源楼层,请求目的楼层>
对于外部请求,当电梯处理该请求之后(该请求出队),要将<请求源楼层,请求目的楼层>中的请求目的楼层加入到请求内部队列(加到队尾)
注意:本次作业类设计必须符合如上要求(包含但不限于设计电梯类、乘客类、队列类以及控制类),凡是不符合类设计要求此题不得分,另外,PTA得分代码界定为第一次提交的最高分代码(因此千万不要把第一次及第二次电梯程序提交到本次题目中测试)。

输入格式:
第一行输入最小电梯楼层数。
第二行输入最大电梯楼层数。
从第三行开始每行输入代表一个乘客请求。

电梯内乘客请求格式:<楼层数>
电梯外乘客请求格式:<请求源楼层,请求目的楼层>,其中,请求源楼层表示乘客发起请求所在的楼层,请求目的楼层表示乘客想要到达的楼层。
当输入“end”时代表输入结束(end不区分大小写)。
输出格式:
模拟电梯的运行过程,输出方式如下:

运行到某一楼层(不需要停留开门),输出一行文本:
Current Floor: 楼层数 Direction: 方向
运行到某一楼层(需要停留开门)输出两行文本:
Open Door # Floor 楼层数
Close Door
输入样例1:
在这里给出一组输入。例如:

1
20
<5,4>
<5>
<7>
end
输出样例1:
在这里给出相应的输出。例如:

Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Open Door # Floor 7
Close Door
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Open Door # Floor 5
Close Door
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
输入样例2:
在这里给出一组输入。例如:

1
20
<5,9>
<8>
<9,3>
<4>
<2>
end
输出样例2:
在这里给出相应的输出。例如:

Current Floor: 1 Direction: UP
Current Floor: 2 Direction: UP
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Open Door # Floor 5
Close Door
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Open Door # Floor 8
Close Door
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Open Door # Floor 4
Close Door
Current Floor: 3 Direction: DOWN
Current Floor: 2 Direction: DOWN
Open Door # Floor 2
Close Door
Current Floor: 3 Direction: UP
Current Floor: 4 Direction: UP
Current Floor: 5 Direction: UP
Current Floor: 6 Direction: UP
Current Floor: 7 Direction: UP
Current Floor: 8 Direction: UP
Current Floor: 9 Direction: UP
Open Door # Floor 9
Close Door
Current Floor: 8 Direction: DOWN
Current Floor: 7 Direction: DOWN
Current Floor: 6 Direction: DOWN
Current Floor: 5 Direction: DOWN
Current Floor: 4 Direction: DOWN
Current Floor: 3 Direction: DOWN
Open Door # Floor 3
Close Door
用Passenger类替换Request类,外部请求改成<起始楼层,目标楼层>,接人后把目标楼层加到内部请求。其他规则差不多。

我的代码
我调整了类:

Elevator:跟之前类似,但初始方向设为UP。
Passenger:存起始和目标楼层,能算方向。
RequestQueue:用31用LinkedList存请求。
Controller:控制电梯,处理请求并加目标楼层。
核心代码(Controller):

点击查看代码
class Controller {
    private Elevator elevator;
    private RequestQueue queue;

    public Controller(Elevator elevator, RequestQueue queue) {
        this.elevator = elevator;
        this.queue = queue;
    }

    public void processRequest() {
        LinkedList<Passenger> externalRequest = queue.getExternalRequests();
        LinkedList<Passenger> internalRequest = queue.getInternalRequests();
        LinkedList<Passenger> tempList = new LinkedList<>();
        while (!externalRequest.isEmpty()) {
            Passenger passenger = externalRequest.poll();
            if (passenger != null && passenger.getSourceFloor() != null) {
                if (passenger.getSourceFloor() == elevator.getCurrentFloor()) {
                    internalRequest.add(new Passenger(passenger.getSourceFloor(), passenger.getDestinationFloor()));
                } else {
                    tempList.add(passenger);
                }
            }
        }
        externalRequest.addAll(tempList);
    }
}
**代码分析**

成果与问题
实现了:
Passenger类像乘客在电梯里选楼层。
初始方向设UP,输出格式好点。
目标楼层能加到内部请求。
未实现:
没去重,重复请求可能加多次。
电梯有时停太早或跑太远,比如<5,9>停在5楼没去9楼。
跑得慢,循环太多。
类图:

踩坑心得
题目集5
错误:没请求时电梯还动,因为没设IDLE。
例子:
1
20
< 3,UP>
end,应停3楼,却到4楼。
修复:在determineDirection加空队列检查,设direction = IDLE。
错误:重复请求没跳过,比如<3,UP><3,UP>在3楼停两次。
例子:
1
20
< 3,UP>
< 3,UP
end,应停一次。
修复:检查队列里是否已有请求,但不完善。
心得:要测奇怪情况(没请求、重复请求),枚举让代码清楚。
题目集6
错误:同一楼层有上行和下行请求时处理乱。
例子:
1
20
< 3,UP>
< 3,DOWN>
end,停靠逻辑错。
修复:改shouldStop仔细检查方向,没全对。
错误:多请求时慢。
例子:复杂请求序列,检查队列太慢。
修复:用LinkedHashSet去重,但方向选择没优化。
心得:拆成类好调试,但要测多组合。
题目集7
错误:首行输出方向是IDLE不是UP。
例子:
1
20
< 5,9>
end
应是Current Floor: 1 Direction: UP。
修复:Elevator初始方向设UP。
错误:没去目标楼层。
例子:
1
20
<5,9>
end,停5楼没去9楼。
修复:processRequest加目标楼层,但时机不对。
心得:大函数拆小块好找错,但循环要小心。
总结:主要问题是漏掉奇怪情况(重复请求、空队列)和跑得慢。拆类后清楚点,但循环和判断还得多优化。

改进方式:
增加时间效率:用排序的请求列表,查得快。
先提前设计好:先列出输入和预期输出,再写代码。
总结
收获:
代码整齐:拆成小类后(题目集6和7),改代码不晕了。
管请求:学会用列表和集合存请求,去掉重复的。
找错:会测怪输入(空请求、重复的),一步步查输出。
想清楚:先想好电梯该干啥,再写,少出错。
不足
测试用例:别漏掉怪情况,比如重复请求。
时间效率:让程序处理多请求不卡。
注释率:多写注释,过段时间还能看懂。

给课程的建议
多给点样例:给点样例代码,比如咋选最近楼层。
教查错:教咋快找到错,用啥工具。
小步走:大问题拆成小块,学着不慌。
具体讲解:希望可以在每一次后具体讲解,不然我如果第一次没过,那我后面接着的几次都很难过了
我的感受
这三电梯题目难爆了,全都没过心态有点炸。但每次修改一个点,感觉自己还是提示了一点的。从啥也不懂,到能写个差不多的程序,我觉得还是挺有成就感的

posted @ 2025-04-20 17:57  紧张cool  阅读(36)  评论(0)    收藏  举报