Skip to content

赛跑 - 题解

标签与难度

标签: 计算几何, 曼哈顿距离, 切比雪夫距离, 坐标变换, 思维题, 最值问题 难度: 1600

题目大意喵~

哈喵~!各位算法大师们好呀!今天本喵要带大家来解决一道有趣的几何问题,喵~

是这样的,有两个可爱的小伙伴,小 L 和小 R,他们都站在二维平面的原点 (0,0)(0,0)。他们发现自己有了超能力,可以在一秒内移动到一个指定的位置!

  • 小 L 有 nn 种移动选择,每种选择对应一个速度向量 (xL,yL)(x_L, y_L),也就是说他一秒后可以到达 (xL,yL)(x_L, y_L)
  • 小 R 也有 mm 种移动选择,每种选择对应一个速度向量 (xR,yR)(x_R, y_R),一秒后可以到达 (xR,yR)(x_R, y_R)

他们想知道,如果小 L 和小 R 各自选择一种移动方式,一秒后他们之间能达到的最大曼哈顿距离是多少呢?

напомню (Reminder) 一下,点 (x1,y1)(x_1, y_1)(x2,y2)(x_2, y_2) 之间的曼哈顿距离是 x1x2+y1y2|x_1 - x_2| + |y_1 - y_2| 哦!

解题思路分析

这道题的目标非常明确:最大化小 L 和小 R 之间的曼哈顿距离。让我们先把目标函数写下来,喵~

假设小 L 选择的终点是 PL=(xL,yL)P_L = (x_L, y_L),小 R 选择的终点是 PR=(xR,yR)P_R = (x_R, y_R)。我们要最大化的就是这个式子:

D=xLxR+yLyRD = |x_L - x_R| + |y_L - y_R|

这个式子里有绝对值,看起来就让人头大,喵呜... 如果要暴力枚举小 L 的 nn 种选择和小 R 的 mm 种选择,那复杂度就是 O(nm)O(nm),当 nnmm 很大的时候,肯定会超时的说。我们得想个更聪明的办法!

处理曼哈顿距离问题,有一个非常经典的技巧,那就是坐标变换!就像猫猫我换个角度看毛线球,问题可能就变简单了呢!我们来尝试一下旋转坐标系45度。

具体来说,我们定义一组新的坐标 (u,v)(u, v)

{u=x+yv=xy\begin{cases} u = x + y \\ v = x - y \end{cases}

这个变换也被称为曼哈顿距离与切比雪夫距离的转换

现在,我们来看看原来的曼哈顿距离公式在新的坐标系下会变成什么样。 我们知道一个性质:对于任意实数 aabb,有 a+b=max(a+b,ab,a+b,ab)|a| + |b| = \max(a+b, a-b, -a+b, -a-b)。 让 a=xLxRa = x_L - x_Rb=yLyRb = y_L - y_R。那么距离 DD 就是:

D=max((xLxR)+(yLyR),(xLxR)(yLyR),)D = \max( (x_L - x_R) + (y_L - y_R), \quad (x_L - x_R) - (y_L - y_R), \quad \dots )

让我们重新组合一下括号里的项:

  1. (xLxR)+(yLyR)=(xL+yL)(xR+yR)=uLuR(x_L - x_R) + (y_L - y_R) = (x_L + y_L) - (x_R + y_R) = u_L - u_R
  2. (xLxR)(yLyR)=(xLyL)(xRyR)=vLvR(x_L - x_R) - (y_L - y_R) = (x_L - y_L) - (x_R - y_R) = v_L - v_R

另外两个表达式只是上面两个的相反数,所以我们可以把它们合并成绝对值的形式!

D=max(uLuR,vLvR)D = \max(|u_L - u_R|, |v_L - v_R|)

哇!看呐!我们成功地把一个麻烦的“和”式距离(曼哈顿距离)变成了一个简单的“最大值”式距离(切比雪夫距离)。在新的 (u,v)(u,v) 坐标系中,两点间的距离是它们在各个坐标轴上差值的最大值。

现在我们的目标变成了最大化 max(uLuR,vLvR)\max(|u_L - u_R|, |v_L - v_R|)。 这等价于:

max(maxi,juLiuRj,maxi,jvLivRj)\max \left( \max_{i,j} |u_{L_i} - u_{R_j}|, \quad \max_{i,j} |v_{L_i} - v_{R_j}| \right)

其中 uLi,vLiu_{L_i}, v_{L_i} 是小 L 的第 ii 个选择变换后的坐标, uRj,vRju_{R_j}, v_{R_j} 是小 R 的第 jj 个选择变换后的坐标。

问题被分解成了两个独立的部分,喵~

  1. 找到最大的 uLuR|u_L - u_R|
  2. 找到最大的 vLvR|v_L - v_R|

最后取这两者的最大值就是答案!

我们先来看第一个子问题:如何最大化 uLuR|u_L - u_R|? 我们有一堆小 L 的 uu 坐标值,记作集合 ULU_L。还有一堆小 R 的 uu 坐标值,记作集合 URU_R。要想让它们之间的差的绝对值最大,我们应该从一个集合里选出最大值,从另一个集合里选出最小值,对吧? 所以,

maxuLuR=max(max(UL)min(UR),max(UR)min(UL))\max |u_L - u_R| = \max(\max(U_L) - \min(U_R), \quad \max(U_R) - \min(U_L))

同理,对于 vv 坐标也是一样的:

maxvLvR=max(max(VL)min(VR),max(VR)min(VL))\max |v_L - v_R| = \max(\max(V_L) - \min(V_R), \quad \max(V_R) - \min(V_L))

好耶!思路清晰了!我们的算法步骤就是:

  1. 初始化四个变量来记录小 L 的 u,vu, v 坐标的最大值和最小值:max_u_L, min_u_L, max_v_L, min_v_L
  2. 遍历小 L 的所有 nn 个速度向量 (x,y)(x, y),计算出对应的 u=x+yu=x+yv=xyv=x-y,并用它们来更新这四个最值。
  3. 对小 R 也做同样的事情,得到 max_u_R, min_u_R, max_v_R, min_v_R
  4. 计算 uu 坐标的最大可能差值:dist_u = max(max_u_L - min_u_R, max_u_R - min_u_L)
  5. 计算 vv 坐标的最大可能差值:dist_v = max(max_v_L - min_v_R, max_v_R - min_v_L)
  6. 最终的答案就是 max(dist_u, dist_v)

这个算法只需要分别遍历一次小 L 和小 R 的所有选择,非常高效,喵~

代码实现

下面就是本喵根据上面的思路,精心准备的C++代码啦!注释写得很详细,希望能帮助你理解哦~

cpp
#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;
}

复杂度分析

  • 时间复杂度: O(N+M)O(N + M) 我们只需要遍历一次小 L 的 NN 个向量和一次小 R 的 MM 个向量来找出 u,vu, v 坐标的极值。之后是常数时间的计算。所以总的时间复杂度是线性的,非常快哦!

  • 空间复杂度: O(1)O(1) 我们只用了几个变量来存储最大值和最小值,没有使用随输入规模增大的额外存储空间。所以空间复杂度是常数级别的,超级节省内存的说!

知识点总结

这道题的核心是一个非常漂亮的小技巧,值得我们记在小本本上,喵~

  1. 曼哈顿距离与切比雪夫距离的转换: 这是解决网格图中曼哈顿距离相关问题的利器!

    • 曼哈顿距离: x1x2+y1y2|x_1 - x_2| + |y_1 - y_2|
    • 切比雪夫距离: max(x1x2,y1y2)\max(|x_1 - x_2|, |y_1 - y_2|)
    • 转换公式: 通过坐标变换 u=x+y,v=xyu=x+y, v=x-y,可以将原坐标系下的曼哈顿距离问题,转化为新坐标系下的切比雪夫距离问题。

      x1x2+y1y2=max(u1u2,v1v2)|x_1 - x_2| + |y_1 - y_2| = \max(|u_1 - u_2|, |v_1 - v_2|)

  2. 最值问题分解: 通过坐标转换,我们将一个耦合的二维最值问题 max(xLxR+yLyR)\max(|x_L - x_R| + |y_L - y_R|),分解成了两个独立的一维最值问题 max(uLuR)\max(|u_L - u_R|)max(vLvR)\max(|v_L - v_R|),大大简化了求解过程。

  3. 一维最值差: 要最大化两个集合中元素之差的绝对值,即 maxab\max|a-b| 其中 aA,bBa \in A, b \in B,只需要计算 max(max(A)min(B),max(B)min(A))\max(\max(A) - \min(B), \max(B) - \min(A)) 即可。

希望这篇题解能帮到你!如果还有不明白的地方,随时可以再来问本喵哦!一起加油,成为更厉害的算法大师吧,喵~!