蓝桥杯[每日一题] 飞机降落(java版)

前言

大家好,我是spring小子!这两天抽时间(说是抽时间,看起来自己很忙,实际上贪玩去了,一天要玩好几个小时哈哈哈)研究了一下DFS(深度优先搜索算法),经过查看多个视频和多篇文章,终于算是搞清楚了。然后看别人的代码,通过问语言大模型来帮助自己理解,结果它们有时候胡言乱语,搞得我越来越迷。最后把别人的代码放IDEA里,通过打断点debug,然后自己画个图,反反复复,最终搞明白了代码的整体逻辑。

问题描述

N架飞机准备降落到某个只有一条跑道的机场。其中第i架飞机在Ti时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 Di个单位时间,即它最早可以于Ti时刻开始降落,最晚可以于Ti+Di时刻开始降落。降落过程需要 Li个单位时间。

一架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不能在前一架飞机完成降落前开始降落。

请你判断N架飞机是否可以全部安全降落

输入格式

输入包含多组数据。

第一行包含一个整数 T,代表测试数据的组数。

对于每组数据,第一行包含一个整数 N。

以下 N 行,每行包含三个整数:Ti,Di 和 Li。

输出格式

对于每组数据,输出 YES 或者 NO,代表是否可以全部安全降落。

样例输入

2
3
0 100 10
10 10 10
0 2 20
3
0 10 20
10 10 20
20 10 20

样例输出

YES
NO

样例说明

对于第一组数据,可以安排第 3架飞机于 0时刻开始降落,20时刻完成降落。安排第 2 架飞机于 20 时刻开始降落,30时刻完成降落。安排第 1架飞机于 30时刻开始降落,40时刻完成降落。

对于第二组数据,无论如何安排,都会有飞机不能及时降落。

思路分析

这道题说实话,大体的思路就是一个暴力枚举:和我们手动判断的过程一样,就是列举每一种可能的情况,然后看看是否存在有符合题意的方案。如果有就输出YES,否则输出NO。

听起来是不是很简单了,没错,大方针确实简单,但我觉得难点就在于,采用DFS来遍历。如果单说DFS的话,好像也不难哦。下面我就以三架飞机为例,来简单讲一下DFS的思想。

如图,我们要先从三架飞机中选择任意一架来降落,然后再从剩余的两架飞机中选择任意一架来降落,最后选择剩余的一架来降落。

注意,这里我们先不考虑能否降落成功,那么是不是就该这样选择? 

好,理解了这个之后,那我们就列举出所有的情况,就是这张图。接下来就一步步地遍历。

比如我们先走下图这条线

 首先尝试降落飞机1,如果能降落,那就继续看飞机2能否降落,如果可以,就继续往下看飞机3。假如飞机3不能降落,那这条线就行不通了,那我们就“原路返回”,回到飞机2,将其标记为没降落,再回到飞机2,标记为未降落。再回到起点,开始下一条线。最终我们就可以列举出所有的情况,如果某一种可行,那么就做一个标记,然后输出YES就可以了。

这就是DFS,即:

从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点,再从另一条路开始走到底,不断递归重复此过程,直到所有的顶点都遍历完成,它的特点是不撞南墙不回头,先走完一条路,再换一条路继续走。

接下来我们讲一下,飞机在何种情况下能够降落。

根据题意,飞机最长等待时间为Ti+Di,如果当前时间比飞机最长等待时间要大,也就是说飞机等不到了,那就不能安全降落;反之则可以降落。能降落分两种情况:

① 跑道空闲,即当前时间小于飞机到达时间,那就直接降落;

②跑道被占用,那么就等待空闲再降落。

这里的“当前时间”其实也可以理解为跑道空闲时的时间,总之就是一个time变量,拿它来与相关数据做比较的。

我们说DFS是“不撞南墙不回头”,本题的“南墙”有两个:一是走到图的末尾,也就是所有飞机都遍历完了;二是当前线路中途就碰到飞机不能降落,那就直接开始下一条路线了。

下面是具体的代码,有详细的注释,大体就是递归:递进和回溯。参考的代码是用的基本变量类型,既然我们是java选手,那就把面向对象用起来!这里我是将每一个飞机的相应数据封装成了一个类,感觉这样数据会工整一些。当然你也可以试试其他方式,这个都不重要,最重要的就是DFS代码的具体实现。

代码实现

import java.util.Scanner;

// 1:无需package
// 2: 类名必须Main, 不可修改
//题目地址:https://www.lanqiao.cn/problems/3511/learning/
public class Main {
    //输入数据组数
    private static int T;
    //每组有几架飞机
    private static int N;
    private static boolean flag = false;
    private static Plane[] planes;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        //在此输入您的代码...
        T = scan.nextInt();
        //对于每一组数据
        while (T-- > 0) {
            N = scan.nextInt();
            planes = new Plane[N];
            for (int i = 0; i < N; i++) {
                int ti = scan.nextInt();
                int di = scan.nextInt();
                int li = scan.nextInt();
                planes[i] = new Plane(ti, di, li);
            }
            //先测试输入有效还是无效:测试结果为有效
            /*for (int i = 0; i < N; i++) {
                System.out.println("第"+(i+1)+"架飞机的数据:"+planes[i].getTi()+" "+planes[i].getDi()+" "+planes[i].getLi());
            }*/
            //有了数据就开始dfs遍历
            dfs(0, 0);
            if (flag) {
                System.out.println("YES");
            } else System.out.println("NO");
            //清空数据,方便下一组数据
            flag = false;
        }
        scan.close();
    }

    /**
     * @param number 当前降落了的飞机数量
     * @param time   当前时间
     */
    private static void dfs(int number, int time) {
        //先写终止条件
        //如果已经降落的飞机数量大于等于已有飞机数量,也就是全都降落完了,就返回去
        if (number >= N) {
            flag = true;
            return;
        }
        //然后就写具体的向下遍历的过程
        //有多少个选择就几层循环
        for (int i = 0; i < N; i++) {
            //如果当前飞机没降落,
            if (!planes[i].islanded) {
                //那就判断一下能否降落:当前时间要小于等于飞机最长等待时间
                if (time <= planes[i].getTi() + planes[i].getDi()) {
                    planes[i].setIslanded(true);
                    //两种降落情况:①跑道空闲,直接降落
                    if (time < planes[i].getTi()) {
                        dfs(number + 1, planes[i].getTi() + planes[i].getLi());
                    }
                    //飞机在等跑道空闲,那空闲之后就直接降落
                    else {
                        dfs(number + 1, time + planes[i].getLi());
                    }
                    //下面的遍历完了,回来之后,将自己标为没降落,尝试下一个方案
                    planes[i].setIslanded(false);
                }
            }
        }
    }

    static class Plane {
        //飞机到达时间
        private int Ti;
        //飞机最大盘旋时间
        private int Di;
        //飞机降落需要的时间
        private int Li;
        //飞机是否降落了
        private boolean islanded;

        public Plane(int ti, int di, int li) {
            Ti = ti;
            Di = di;
            Li = li;
        }

        public int getTi() {
            return Ti;
        }

        public void setIslanded(boolean islanded) {
            this.islanded = islanded;
        }

        public int getDi() {
            return Di;
        }

        public int getLi() {
            return Li;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值