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)问题的变种。但基德同志可以选择两个方向逃跑,所以我们需要考虑两种情况:
-
向左逃跑:相当于从右往左找最长下降子序列
-
向右逃跑:相当于从左往右找最长下降子序列
我们的解决方案是使用动态规划这个"瑞士军刀":
-
创建两个DP数组:
dp_left和dp_right -
dp_left[i]表示以第i栋楼为起点,向左逃跑(即原序列中向右)的最长下降子序列长度 -
dp_right[i]表示以第i栋楼为起点,向右逃跑(即原序列中向左)的最长下降子序列长度 -
最后取两个数组中的最大值作为答案
这就像给基德配备了两位导航员,一位专门规划向左逃跑的路线,一位规划向右的路线,最后选择更好的那个方案。
代码分析:基德的逃跑路线规划器
#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;
} // 完结撒花 (~ ̄▽ ̄)~
这段代码就像给基德装了一个逃跑路线计算器:
-
首先读取楼的高度数据
-
然后分别计算两个方向的"最优逃跑路线":
-
dp_left计算从左到右的最长下降子序列 -
dp_right计算从右到左的最长下降子序列
-
-
最后比较两个方向的结果,取最大值输出
有趣的是,虽然我们说是"向左逃跑"和"向右逃跑",但实际上在代码中是通过改变遍历方向来实现的。这就像告诉基德:"往左跑其实是按右方向键",这种反直觉的设计真是999啊!
总结:优雅的小偷需要优雅的算法
通过这个题目,我们不仅学会了如何用动态规划解决最长下降子序列问题,还学会了如何考虑问题的两个方向。下次如果你看到有个穿白西装的人在高楼间滑翔,别忘了问他:"嘿,老兄,你的DP数组更新对了吗?"
记住,一个好的程序员不仅要会写代码,还要能在关键时刻帮国际大盗规划逃跑路线——谁知道这会不会成为你简历上的亮点呢?😉
对了,三连点了吗?
459

被折叠的 条评论
为什么被折叠?



