试题 D: 飞机降落

本文通过分析试题D:飞机降落,探讨如何判断飞机是否能在给定的限制条件下安全降落。内容包括信息分析、算法设计(贪心算法和深度优先搜索)、代码实现、遇到的问题以及学习到的思想和方法。最后指出贪心算法的局限性和DFS的适用性,强调解决问题时对全局理解和算法选择的重要性。
摘要由CSDN通过智能技术生成

试题 D: 飞机降落

时间限制:  2.0s      内存限制:  256.0MB      本题总分: 10 

【问题描述】

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

试题D: 飞机降落                                                                    6

0 10 20

10 10 20

20 10 20

【样例输出】

YES

NO

【样例说明】

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

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

【评测用例规模与约定】

对于 30% 的数据, N 2

对于 100%  的数据, 1  T  10 1 N  10 0  Ti ; Di ; Li   105

我的答案:

1. 信息

  • 飞机数量 N:需要降落的飞机总数。
  • 到达时间 Ti​:第 i 架飞机到达跑道上空的时间。
  • 盘旋时间 Di​:第 i 架飞机可以盘旋的时间。
  • 降落时间 Li​:第 i 架飞机降落所需的时间。

2. 分析

  • 目标:判断所有飞机是否都可以在它们的盘旋时间内安全降落。
  • 关键思路:飞机必须在 Ti​ 到 +Ti​+Di​ 的时间内开始降落。我们需要找到一种降落顺序,使得每架飞机都能在这个时间窗口内降落。
  • 策略:将飞机按照它们最晚开始降落的时间Ti​+Di​ 进行排序,然后按此顺序尝试安排它们降落。

3. 算法设计

  1. 将所有飞机按照 Ti​+Di​ 升序排序。
  2. 遍历排序后的飞机列表,记录当前时间 currentTime。
  3. 对于每架飞机,检查是否可以在 Ti​ 到Ti​+Di​ 时间内降落。
    • 如果 currentTime≤Ti​,则该飞机可以在 Ti​ 时刻开始降落。
    • 否则,检查 currentTime 是否在 Ti​ 和 Ti​+Di​ 之间。如果是,则该飞机可以降落;如果不是,则返回 "NO"。
  4. 更新 currentTime 为当前飞机降落结束的时间。
  5. 如果所有飞机都能安排降落,则返回 "YES"。

4. 代码实现(用C++)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Plane {
    int T, D, L;
};

bool canLandAll(vector<Plane>& planes) {
    sort(planes.begin(), planes.end(), [](const Plane& a, const Plane& b) {
        return a.T + a.D < b.T + b.D;
    });

    int currentTime = 0;
    for (const auto& plane : planes) {
        if (currentTime < plane.T) currentTime = plane.T;
        if (currentTime > plane.T + plane.D) return false;
        currentTime += plane.L;
    }
    return true;
}

int main() {
    int T, N;
    cin >> T;
    while (T--) {
        cin >> N;
        vector<Plane> planes(N);
        for (int i = 0; i < N; ++i) {
            cin >> planes[i].T >> planes[i].D >> planes[i].L;
        }
        cout << (canLandAll(planes) ? "YES" : "NO") << endl;
    }
    return 0;
}

5. 实现代码过程中可能遇到的问题

  • 排序复杂度:对飞机进行排序的时间复杂度是 O(NlogN),对于大量飞机的情况可能会有性能影响。
  • 时间管理:在更新 currentTime 时需要正确处理每架飞机的降落时间。
  • 边界情况处理:需要确保所有边界情况都被正确处理,例如当 currentTime 正好等于 T_i + D_i 时的情况。

失败了,😄没通过所有案例,有读者知道为什么吗?

官方题解:

1. 信息

  • 飞机数量 N:需要降落的飞机总数。
  • 到达时间 Ti​:第 i 架飞机到达跑道上空的时间。
  • 盘旋时间 Di​:第 i 架飞机可以盘旋的时间。
  • 降落时间 Li​:第 i 架飞机降落所需的时间。

2. 分析

  • 目标:判断所有飞机是否都可以在它们的盘旋时间内安全降落。
  • 关键思路:考虑所有可能的飞机降落顺序,以确保每架飞机都能在其可盘旋的时间窗口内安全降落。
  • 决策过程:使用深度优先搜索(DFS)遍历所有可能的飞机降落顺序。对于每一种顺序,检查是否满足每架飞机的最早和最晚降落时间要求。

3. 算法设计

  • 使用DFS遍历所有可能的飞机降落顺序。
  • 对于每一种顺序,记录当前时间,并检查每架飞机是否能在其允许的时间内降落。
  • 如果所有飞机都能安排降落,则返回“是”;如果任何一架飞机无法在规定时间内降落,则返回“否”。

4. 代码实现(用C++)

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 15;

int n;
int a[N], b[N], c[N]; // 分别存储到达时间、盘旋时间和降落时间
bool st[N]; // 标记某架飞机是否已被安排降落

bool dfs(int u, int last, int cnt) {
    if (b[u] < last) return false; // 检查是否满足最晚降落时间要求
    if (cnt == n) return true; // 所有飞机都已安排降落
    
    for (int i = 0; i < n; ++i) {
        if (!st[i]) {
            st[i] = true;
            if (dfs(i, max(a[u], last) + c[u], cnt + 1)) return true;
            st[i] = false;
        }
    }
    return false;
}

int main() {
    int T;
    cin >> T;
    
    while (T--) {
        memset(st, 0, sizeof st);
        cin >> n;
        for (int i = 0; i < n; ++i)
            cin >> a[i] >> b[i] >> c[i], b[i] += a[i];
        
        bool flag = false;
        for (int i = 0; i < n; ++i) {
            st[i] = true;
            if (dfs(i, 0, 1)) {
                flag = true;
                break;
            }
            st[i] = false;
        }
        
        cout << (flag ? "YES" : "NO") << endl;
    }
    
    return 0;
}

5. 实现代码过程中可能遇到的问题 

  • 时间复杂度:由于DFS算法遍历了所有可能的飞机降落顺序,其时间复杂度为指数级,可能导致在大数据量时运行时间过长。
  • 栈溢出:递归深度过大可能会导致栈溢出,尤其是在处理较大规模问题时。
  • 初始化状态:需要确保每次开始新的DFS前,所有状态(如 st[] 数组)都已正确初始化。
  • 边界情况处理:在实现DFS过程中,需要仔细处理每一架飞机的时间窗口边界情况,以避免错误的判断。

六、学到了什么?

  1. 问题分析和理解:首先,要准确理解问题的需求和限制条件。这个问题需要我们安排一系列飞机在特定的时间窗口内降落,考虑到每架飞机的到达时间、盘旋时间和降落时间。正确理解这些参数及其相互关系是解决问题的第一步。

  2. 算法选择的重要性:这个问题展示了选择合适的算法来解决特定问题的重要性。最初尝试的贪心算法虽然简单高效,但可能无法处理所有情况,特别是那些需要全局最优解的复杂情况。而深度优先搜索(DFS)虽然可能效率较低,却能提供全局最优解,确保所有飞机都能在规定时间内安全降落。

  3. 递归和深度优先搜索(DFS):这个问题是深度优先搜索的一个很好的应用实例。通过递归地探索所有可能的降落顺序,DFS算法可以找到一个符合所有限制条件的解决方案。

  4. 边界条件和状态管理:在实现DFS的过程中,我们需要仔细处理边界条件(如飞机的最早和最晚降落时间)并妥善管理状态(如标记哪些飞机已被安排降落)。这对于防止错误和确保算法正确性是至关重要的。

  5. 时间复杂度和效率考虑:此问题还强调了考虑算法的时间复杂度和运行效率的重要性。对于有限的数据集,DFS是可行的,但随着数据规模的增大,效率可能会成为一个问题。

  6. 实际应用中的决策制定:这个问题模拟了现实世界中的一种决策情景,即如何在有限资源(此处为跑道和时间)和严格限制(飞机的盘旋时间)下进行有效的调度。这类问题在许多领域,如物流、交通管理等,都非常常见。

思想和方法:

  1. 数学建模:将现实世界的问题转化为数学模型是解决复杂问题的第一步。在这个例子中,我们将飞机的降落问题转化为一个调度问题,其中涉及时间窗口、排序和优化。

  2. 排序和优化:通过分析问题,我们理解到排序飞机降落时间可以帮助找到解决方案。这是一种数学思维方式,即通过组织和排序数据来简化问题。

  3. 递归思维:深度优先搜索(DFS)算法的应用展示了递归思维的力量。递归是一种重要的数学思维方式,它允许我们将一个复杂问题分解为更小、更易管理的子问题。

  4. 探索所有可能性:使用DFS算法来探索所有可能的降落顺序,这是一种穷尽搜索的方法。这种方法体现了一种数学思维,即在某些情况下,最好的方式是检查所有可能的情况以找到解决方案。

  5. 时间和资源的约束处理:在这个问题中,我们必须在特定的时间和资源(跑道可用性)约束下进行调度。学习如何在有限的约束条件下优化结果是数学中的一个重要方面。

  6. 算法的时间复杂度分析:理解不同算法(如贪心算法和DFS)的时间复杂度对于选择正确的方法来解决问题至关重要。这是数学中对算法效率的分析。

  7. 权衡和决策:在选择使用贪心算法还是DFS时,我们进行了一种权衡,这体现了数学中的决策制定过程。权衡算法的复杂性和解决问题的准确性是数学思维的一个重要方面。

七、犯了什么错误?

1. 贪心算法的局限性

  • 局部最优:贪心算法通常寻找局部最优解,这在某些问题中可能并不等同于全局最优解。在这个问题中,我建议根据飞机的最晚开始降落时间来排序,这在一些情况下可能无法考虑所有飞机的最佳降落顺序。

  • 忽略全局上下文:我没有充分考虑所有飞机的全局降落时间表,这可能导致无法找到适应所有飞机的最佳降落顺序。

2. 没有考虑更复杂的算法

在最初的解答中,我没有考虑到更复杂但能够提供全局解的算法,如深度优先搜索(DFS)。DFS能够考虑所有可能的降落顺序,从而更有可能找到满足所有条件的解决方案。

3. 对问题复杂性的低估

这个问题比最初看起来的更复杂,因为它涉及到对多个有时间限制的事件进行调度。在这种情况下,一个简单的贪心策略可能不足以解决所有潜在的复杂情况。

结论

这个错误强调了在解决复杂问题时对问题的全面理解的重要性,以及选择合适算法的重要性。在解决实际问题时,理解问题的所有细节和复杂性,并选择一个能够全面解决问题的算法是至关重要的。同时,这也显示了计算机科学和算法设计中常见的挑战,即如何在算法的复杂性和解决问题的有效性之间找到平衡。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值