赛跑 - 题解
标签与难度
标签: 计算几何, 曼哈顿距离, 切比雪夫距离, 坐标变换, 思维题, 最值问题 难度: 1600
题目大意喵~
哈喵~!各位算法大师们好呀!今天本喵要带大家来解决一道有趣的几何问题,喵~
是这样的,有两个可爱的小伙伴,小 L 和小 R,他们都站在二维平面的原点 。他们发现自己有了超能力,可以在一秒内移动到一个指定的位置!
- 小 L 有 种移动选择,每种选择对应一个速度向量 ,也就是说他一秒后可以到达 。
- 小 R 也有 种移动选择,每种选择对应一个速度向量 ,一秒后可以到达 。
他们想知道,如果小 L 和小 R 各自选择一种移动方式,一秒后他们之间能达到的最大曼哈顿距离是多少呢?
напомню (Reminder) 一下,点 和 之间的曼哈顿距离是 哦!
解题思路分析
这道题的目标非常明确:最大化小 L 和小 R 之间的曼哈顿距离。让我们先把目标函数写下来,喵~
假设小 L 选择的终点是 ,小 R 选择的终点是 。我们要最大化的就是这个式子:
这个式子里有绝对值,看起来就让人头大,喵呜... 如果要暴力枚举小 L 的 种选择和小 R 的 种选择,那复杂度就是 ,当 和 很大的时候,肯定会超时的说。我们得想个更聪明的办法!
处理曼哈顿距离问题,有一个非常经典的技巧,那就是坐标变换!就像猫猫我换个角度看毛线球,问题可能就变简单了呢!我们来尝试一下旋转坐标系45度。
具体来说,我们定义一组新的坐标 :
这个变换也被称为曼哈顿距离与切比雪夫距离的转换。
现在,我们来看看原来的曼哈顿距离公式在新的坐标系下会变成什么样。 我们知道一个性质:对于任意实数 和 ,有 。 让 ,。那么距离 就是:
让我们重新组合一下括号里的项:
另外两个表达式只是上面两个的相反数,所以我们可以把它们合并成绝对值的形式!
哇!看呐!我们成功地把一个麻烦的“和”式距离(曼哈顿距离)变成了一个简单的“最大值”式距离(切比雪夫距离)。在新的 坐标系中,两点间的距离是它们在各个坐标轴上差值的最大值。
现在我们的目标变成了最大化 。 这等价于:
其中 是小 L 的第 个选择变换后的坐标, 是小 R 的第 个选择变换后的坐标。
问题被分解成了两个独立的部分,喵~
- 找到最大的 。
- 找到最大的 。
最后取这两者的最大值就是答案!
我们先来看第一个子问题:如何最大化 ? 我们有一堆小 L 的 坐标值,记作集合 。还有一堆小 R 的 坐标值,记作集合 。要想让它们之间的差的绝对值最大,我们应该从一个集合里选出最大值,从另一个集合里选出最小值,对吧? 所以,
同理,对于 坐标也是一样的:
好耶!思路清晰了!我们的算法步骤就是:
- 初始化四个变量来记录小 L 的 坐标的最大值和最小值:
max_u_L,min_u_L,max_v_L,min_v_L。 - 遍历小 L 的所有 个速度向量 ,计算出对应的 和 ,并用它们来更新这四个最值。
- 对小 R 也做同样的事情,得到
max_u_R,min_u_R,max_v_R,min_v_R。 - 计算 坐标的最大可能差值:
dist_u = max(max_u_L - min_u_R, max_u_R - min_u_L)。 - 计算 坐标的最大可能差值:
dist_v = max(max_v_L - min_v_R, max_v_R - min_v_L)。 - 最终的答案就是
max(dist_u, dist_v)!
这个算法只需要分别遍历一次小 L 和小 R 的所有选择,非常高效,喵~
代码实现
下面就是本喵根据上面的思路,精心准备的C++代码啦!注释写得很详细,希望能帮助你理解哦~
#include <iostream>
#include <algorithm>
#include <vector>
// 使用 long long 防止坐标和或差溢出
using ll = long long;
// 定义一个很大的数作为初始的最小值
const ll INF = 4e9 + 7; // 坐标最大10^9, 和/差最大2*10^9, 用一个更大的数
int main() {
// 加速输入输出,让程序跑得像猫一样快,喵~
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int n, m;
std::cin >> n >> m;
// 初始化小 L 的 u, v 坐标范围
// u = x + y, v = x - y
ll max_u_L = -INF, min_u_L = INF;
ll max_v_L = -INF, min_v_L = INF;
// 遍历小 L 的所有可选向量
for (int i = 0; i < n; ++i) {
ll x, y;
std::cin >> x >> y;
ll u = x + y;
ll v = x - y;
// 更新 u 和 v 的最值
max_u_L = std::max(max_u_L, u);
min_u_L = std::min(min_u_L, u);
max_v_L = std::max(max_v_L, v);
min_v_L = std::min(min_v_L, v);
}
// 初始化小 R 的 u, v 坐标范围
ll max_u_R = -INF, min_u_R = INF;
ll max_v_R = -INF, min_v_R = INF;
// 遍历小 R 的所有可选向量
for (int i = 0; i < m; ++i) {
ll x, y;
std::cin >> x >> y;
ll u = x + y;
ll v = x - y;
// 更新 u 和 v 的最值
max_u_R = std::max(max_u_R, u);
min_u_R = std::min(min_u_R, u);
max_v_R = std::max(max_v_R, v);
min_v_R = std::min(min_v_R, v);
}
// 计算 u 坐标系下的最大距离
// 可能是 L 的最大值减 R 的最小值,也可能是 R 的最大值减 L 的最小值
ll max_dist_u = std::max(max_u_L - min_u_R, max_u_R - min_u_L);
// 计算 v 坐标系下的最大距离
ll max_dist_v = std::max(max_v_L - min_v_R, max_v_R - min_v_L);
// 最终答案是两种坐标系下最大距离的较大者
std::cout << std::max(max_dist_u, max_dist_v) << std::endl;
return 0;
}复杂度分析
时间复杂度: 我们只需要遍历一次小 L 的 个向量和一次小 R 的 个向量来找出 坐标的极值。之后是常数时间的计算。所以总的时间复杂度是线性的,非常快哦!
空间复杂度: 我们只用了几个变量来存储最大值和最小值,没有使用随输入规模增大的额外存储空间。所以空间复杂度是常数级别的,超级节省内存的说!
知识点总结
这道题的核心是一个非常漂亮的小技巧,值得我们记在小本本上,喵~
曼哈顿距离与切比雪夫距离的转换: 这是解决网格图中曼哈顿距离相关问题的利器!
- 曼哈顿距离:
- 切比雪夫距离:
- 转换公式: 通过坐标变换 ,可以将原坐标系下的曼哈顿距离问题,转化为新坐标系下的切比雪夫距离问题。
最值问题分解: 通过坐标转换,我们将一个耦合的二维最值问题 ,分解成了两个独立的一维最值问题 和 ,大大简化了求解过程。
一维最值差: 要最大化两个集合中元素之差的绝对值,即 其中 ,只需要计算 即可。
希望这篇题解能帮到你!如果还有不明白的地方,随时可以再来问本喵哦!一起加油,成为更厉害的算法大师吧,喵~!