怪盗基德的“坠楼“计划:如何优雅地滑翔而不摔成煎饼?

Jambo!我是Zac,今天我们要聊一个既刺激又有趣的算法问题——怪盗基德的滑翔大冒险!这位穿白西装的绅士小偷这次遇到了大麻烦,他的滑翔翼坏了,只能往下滑。作为他的"技术支援",我们得帮他规划一条最拉风的逃跑路线!

题目描述

小偷的需求

怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯。而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。 有一天,怪盗基德像往常一样偷走了一颗珍贵的钻石,不料却被柯南小朋友识破了伪装,而他的滑翔翼的动力装置也被柯南踢出的足球破坏了。不得已,怪盗基德只能操作受损的滑翔翼逃脱。 假设城市中一共有N幢建筑排成一条线,每幢建筑的高度各不相同。初始时,怪盗基德可以在任何一幢建筑的顶端。他可以选择一个方向逃跑,但是不能中途改变方向(因为中森警部会在后面追击)。因为滑翔翼动力装置受损,他只能往下滑行(即:只能从较高的建筑滑翔到较低的建筑)。他希望尽可能多地经过不同建筑的顶部,这样可以减缓下降时的冲击力,减少受伤的可能性。请问,他最多可以经过多少幢不同建筑的顶部(包含初始时的建筑)?

输入

输入数据第一行是一个整数K(K<100),代表有K组测试数据。 每组测试数据包含两行:第一行是一个整数N(N<100),代表有N幢建筑。第二行包含N个不同的整数,每一个对应一幢建筑的高度h[0]。  

(想要样例吗?先看完再说!)

输出

对于每一组测试数据,输出一行,包含一个整数,代表怪盗基德最多可以经过的建筑数量。

(想要样例吗?先看完再说!)

你们的样例来了

IN

3
8
300 207 155 299 298 170 158 65
8
65 158 170 298 299 155 207 300
10
2 1 3 4 5 6 7 8 9 10

OUT

6
6
9

题目分析:小偷的烦恼

想象一下,怪盗基德站在一排高低不一的摩天大楼顶上(为什么小偷总能找到这么酷的场地?),他可以选择向左或向右逃跑,但一旦选定方向就不能回头(毕竟警部在后面追呢)。由于滑翔翼坏了,他只能从高的楼滑到低的楼。我们的任务是帮他找出:在最优逃跑路线下,最多能经过多少栋不同的楼?

输入会告诉我们楼的数量和每栋楼的高度,输出则是最多能经过的楼数。简单来说,就是在一个序列中找一个最长下降子序列,但可以选择从左往右或从右往左找。

思路分析:动态规划的"双面间谍"

这个问题本质上是最长下降子序列(LDS)问题的变种。但基德同志可以选择两个方向逃跑,所以我们需要考虑两种情况:

  1. 向左逃跑:相当于从右往左找最长下降子序列

  2. 向右逃跑:相当于从左往右找最长下降子序列

我们的解决方案是使用动态规划这个"瑞士军刀":

  1. 创建两个DP数组:dp_leftdp_right

  2. dp_left[i]表示以第i栋楼为起点,向左逃跑(即原序列中向右)的最长下降子序列长度

  3. dp_right[i]表示以第i栋楼为起点,向右逃跑(即原序列中向左)的最长下降子序列长度

  4. 最后取两个数组中的最大值作为答案

这就像给基德配备了两位导航员,一位专门规划向左逃跑的路线,一位规划向右的路线,最后选择更好的那个方案。

代码分析:基德的逃跑路线规划器

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

int main() {
    int K;
    cin >> K;  // 测试数据组数
    while (K--) {
        int N;
        cin >> N;  // 建筑数量
        vector<int> height(N);
        for (int i = 0; i < N; ++i) {
            cin >> height[i];  // 读取每栋楼的高度
        }
        
        // 向左逃跑的DP数组(实际上是原序列从左到右的LDS)
        vector<int> dp_left(N, 1);  // 初始化为1,因为至少可以停在当前建筑
        for (int i = 1; i < N; ++i) {
            for (int j = 0; j < i; ++j) {
                if (height[j] > height[i]) {  // 只有前面的楼更高才能滑过去
                    dp_left[i] = max(dp_left[i], dp_left[j] + 1);
                }
            }
        }

        // 向右逃跑的DP数组(实际上是原序列从右到左的LDS)
        vector<int> dp_right(N, 1);
        for (int i = N - 2; i >= 0; --i) {
            for (int j = N - 1; j > i; --j) {
                if (height[j] > height[i]) {  // 只有后面的楼更高才能滑过去
                    dp_right[i] = max(dp_right[i], dp_right[j] + 1);
                }
            }
        }
        
        // 找出两个方向中的最大值
        int result = 0;
        for (int i = 0; i < N; ++i) {
            result = max(result, max(dp_left[i], dp_right[i]));
        }
        cout << result << endl;
    }
    return 0;
} // 完结撒花 (~ ̄▽ ̄)~

这段代码就像给基德装了一个逃跑路线计算器:

  1. 首先读取楼的高度数据

  2. 然后分别计算两个方向的"最优逃跑路线":

    • dp_left计算从左到右的最长下降子序列

    • dp_right计算从右到左的最长下降子序列

  3. 最后比较两个方向的结果,取最大值输出

有趣的是,虽然我们说是"向左逃跑"和"向右逃跑",但实际上在代码中是通过改变遍历方向来实现的。这就像告诉基德:"往左跑其实是按右方向键",这种反直觉的设计真是999啊!

总结:优雅的小偷需要优雅的算法

通过这个题目,我们不仅学会了如何用动态规划解决最长下降子序列问题,还学会了如何考虑问题的两个方向。下次如果你看到有个穿白西装的人在高楼间滑翔,别忘了问他:"嘿,老兄,你的DP数组更新对了吗?"

记住,一个好的程序员不仅要会写代码,还要能在关键时刻帮国际大盗规划逃跑路线——谁知道这会不会成为你简历上的亮点呢?😉

对了,三连点了吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值