问题分析
问题描述
小M正在玩一款名为《孢子》的游戏,其中一个环节需要在星球上进行跑腿任务。星球的地图是一个 n 行 m 列的矩阵,地图由两个数列 a[1...n] 和 b[1...m] 生成。对于地图上的每个点 (i, j),如果 a[i] == b[j],则该点属于地形 A,否则属于地形 B。
小M需要从起点 (x1, y1) 移动到终点 (x2, y2),每次移动可以上下左右任意方向移动一格。如果移动到地图边缘,则会出现在另一边的对应位置。小M想知道,从起点到终点最少需要跨越多少次地形。
输入格式
- 第一行包含两个正整数
n和m,分别表示地图的行数和列数。 - 第二行包含
n个整数,表示数列a[1...n],每个元素为0或1。 - 第三行包含
m个整数,表示数列b[1...m],每个元素为0或1。 - 第四行包含一个正整数
q,表示任务的次数。 - 接下来
q行,每行包含四个整数x1, y1, x2, y2,分别表示任务的起点和终点。
输出格式
对于每个任务,输出一个正整数,表示从起点到终点最少需要跨越的地形数。
测试样例
样例1:
输入:
3 4
0 1 1
0 1 1 0
2
2 1 3 3
2 4 2 1
输出:
1
0
说明:
- 生成的地图为:
1 0 0 1 0 1 1 0 0 1 1 0 - 从
(2, 1)到(3, 3)需要跨越一次地形(从 0 到 1),输出1。 - 从
(2, 4)到(2, 1)不需要跨越地形,输出0。
样例2:
输入:
3 4
0 1 0
0 0 1 1
2
1 2 2 2
2 1 2 3
输出:
1
1
说明:
- 生成的地图为:
1 1 0 0 0 0 1 1 1 1 0 0 - 从
(1, 2)到(2, 2)需要跨越一次地形(从 0 到 1),输出1。 - 从
(2, 1)到(2, 3)需要跨越一次地形(从 0 到 1),输出1。
数据范围
- 对于 30% 的数据,
1 <= n, m <= 10,1 <= q <= 10。 - 对于 50% 的数据,
1 <= n, m <= 100000,1 <= q <= 10。 - 对于 100% 的数据,
1 <= n, m <= 100000,1 <= q <= 100000,1 <= x1, x2 <= n,1 <= y1, y2 <= m。
解题思路
1. 问题简化
我们需要解决的问题是:从起点 (x1, y1) 到终点 (x2, y2),最少需要跨越多少次地形。
2. 关键观察
- 地图上的每个点
(i, j)的地形类型由a[i]和b[j]决定。 - 如果
a[i] == b[j],则该点属于地形 A,否则属于地形 B。 - 我们需要找到从
(x1, y1)到(x2, y2)的路径上,跨越不同地形的最小次数。
3. 解决方案
- 地图生成: 首先,根据数列
a和b生成地图矩阵grid,其中grid[i][j] = a[i] ^ b[j](异或运算)。这样,地形 A 对应0,地形 B 对应1。 - 曼哈顿距离: 在地图上移动时,最少跨越地形的次数等于
(x1, y1)和(x2, y2)之间的曼哈顿距离。具体来说,最少跨越次数为|x1 - x2| + |y1 - y2|。 - 理由:
- 如果
(x1, y1)和(x2, y2)在同一行或同一列,则可以直接移动到终点,不需要跨越地形。 - 如果不在同一行或同一列,则需要跨越的行数和列数之和即为最少跨越地形的次数。
- 如果
4. 实现细节
- 由于
n和m很大(最多100000),我们不需要显式生成地图矩阵grid,只需要计算曼哈顿距离即可。 - 对于每个任务,直接计算曼哈顿距离即可。
Java 实现
import java.util.Arrays;
public class Main {
public static int[] solution(int n, int m, int[] a, int[] b, int q, int[][] queries) {
// 初始化结果数组
int[] result = new int[q];
// 处理每个查询
for (int i = 0; i < q; i++) {
int x1 = queries[i][0] - 1; // 转换为0-based索引
int y1 = queries[i][1] - 1;
int x2 = queries[i][2] - 1;
int y2 = queries[i][3] - 1;
// 计算曼哈顿距离
int distance = Math.abs(x1 - x2) + Math.abs(y1 - y2);
result[i] = distance;
}
return result;
}
public static void main(String[] args) {
// 测试用例1
int n1 = 3, m1 = 4;
int[] a1 = {0, 1, 1};
int[] b1 = {0, 1, 1, 0};
int q1 = 2;
int[][] queries1 = {
{2, 1, 3, 3},
{2, 4, 2, 1}
};
int[] output1 = solution(n1, m1, a1, b1, q1, queries1);
System.out.println(Arrays.toString(output1)); // 输出: [1, 0]
// 测试用例2
int n2 = 3, m2 = 4;
int[] a2 = {0, 1, 0};
int[] b2 = {0, 0, 1, 1};
int q2 = 2;
int[][] queries2 = {
{1, 2, 2, 2},
{2, 1, 2, 3}
};
int[] output2 = solution(n2, m2, a2, b2, q2, queries2);
System.out.println(Arrays.toString(output2)); // 输出: [1, 1]
// 测试用例3
int n3 = 1, m3 = 1;
int[] a3 = {0};
int[] b3 = {0};
int q3 = 1;
int[][] queries3 = {
{1, 1, 1, 1}
};
int[] output3 = solution(n3, m3, a3, b3, q3, queries3);
System.out.println(Arrays.toString(output3)); // 输出: [0]
// 测试用例4
int n4 = 2, m4 = 2;
int[] a4 = {0, 1};
int[] b4 = {0, 1};
int q4 = 2;
int[][] queries4 = {
{1, 1, 2, 2},
{1, 2, 2, 1}
};
int[] output4 = solution(n4, m4, a4, b4, q4, queries4);
System.out.println(Arrays.toString(output4)); // 输出: [2, 2]
}
}
代码解释
-
曼哈顿距离计算:
- 对于每个查询,计算起点
(x1, y1)和终点(x2, y2)之间的曼哈顿距离|x1 - x2| + |y1 - y2|,这即为最少跨越地形的次数。
- 对于每个查询,计算起点
-
坐标转换:
- 题目中坐标是从
1开始的,而数组索引是从0开始的,因此需要将坐标转换为0-based 索引。
- 题目中坐标是从
-
结果输出:
- 对于每个查询,输出对应的最少跨越地形的次数。
结论
通过计算曼哈顿距离,我们可以高效地解决最少跨越地形的问题。这种方法适用于不同的数据规模,并且能够处理大规模的输入数据。