网格中连通块的最小距离求解
问题定义
在二维网格中,连通块指由相邻(通常为四连通:上下左右) 的相同元素组成的区域。求解连通块的最小距离,即找到两个不同连通块中任意两点之间的最短距离(常用曼哈顿距离或欧氏距离定义)。
核心思路
- 识别连通块:遍历网格,通过 BFS 或 DFS 标记所有连通块,记录每个连通块包含的所有坐标。
- 计算块间距离:对每对不同的连通块,计算其内部所有点对的距离,取最小值作为这两个块的距离。
- 全局最小距离:在所有块间距离中取最小值,即为结果。
关键细节
- 连通性:默认四连通(上下左右),如需八连通(含对角线)可调整方向数组。
- 距离定义:
- 曼哈顿距离:∣x1−x2∣+∣y1−y2∣|x_1 - x_2| + |y_1 - y_2|∣x1−x2∣+∣y1−y2∣(网格中移动步数,常用)。
- 欧氏距离:(x1−x2)2+(y1−y2)2\sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}(x1−x2)2+(y1−y2)2(直线距离)。
- 过滤条件:通常只考虑目标连通块(如非背景值,如排除 0)。
代码实现
完整代码
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <cmath>
using namespace std;
// 四连通方向(上下左右)
const vector<pair<int, int>> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// BFS 识别连通块并记录坐标
void bfs(int start_x, int start_y, int block_id,
const vector<vector<int>>& grid,
vector<vector<int>>& visited,
vector<vector<pair<int, int>>>& blocks) {
int m = grid.size();
int n = grid[0].size();
queue<pair<int, int>> q;
q.push({start_x, start_y});
visited[start_x][start_y] = block_id;
blocks[block_id].push_back({start_x, start_y});
int target_val = grid[start_x][start_y]; // 连通块的元素值
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
for (auto [dx, dy] : dirs) {
int nx = x + dx;
int ny = y + dy;
// 检查边界、未访问、且值相同
if (nx >= 0 && nx < m && ny >= 0 && ny < n
&& visited[nx][ny] == -1
&& grid[nx][ny] == target_val) {
visited[nx][ny] = block_id;
q.push({nx, ny});
blocks[block_id].push_back({nx, ny});
}
}
}
}
// 计算两个连通块之间的最小曼哈顿距离
int minDistanceBetweenTwoBlocks(const vector<pair<int, int>>& block1,
const vector<pair<int, int>>& block2) {
int min_dist = INT_MAX;
for (const auto& p1 : block1) {
for (const auto& p2 : block2) {
int dist = abs(p1.first - p2.first) + abs(p1.second - p2.second); // 曼哈顿距离公式
if (dist < min_dist) {
min_dist = dist;
}
}
}
return min_dist;
}
// 求解网格中所有连通块的最小距离
int minDistanceOfBlocks(const vector<vector<int>>& grid, int ignore_val = 0) {
int m = grid.size();
if (m == 0) return -1;
int n = grid[0].size();
if (n == 0) return -1;
vector<vector<int>> visited(m, vector<int>(n, -1)); // 标记所属连通块ID
vector<vector<pair<int, int>>> blocks; // 存储每个连通块的所有坐标
int block_id = 0;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
// 只处理未访问且非忽略值的元素
if (visited[i][j] == -1 && grid[i][j] != ignore_val) {
blocks.emplace_back(); // 新增一个连通块
bfs(i, j, block_id, grid, visited, blocks);
block_id++;
}
}
}
// 不足两个连通块时返回-1
if (blocks.size() < 2) {
return -1;
}
// 计算所有 pairs 连通块的最小距离
int global_min = INT_MAX;
for (int i = 0; i < blocks.size(); ++i) {
for (int j = i + 1; j < blocks.size(); ++j) {
int dist = minDistanceBetweenTwoBlocks(blocks[i], blocks[j]);
if (dist < global_min) {
global_min = dist;
}
}
}
return global_min;
}
int main() {
// 示例网格
vector<vector<int>> grid = {
{1, 1, 0, 0},
{1, 0, 0, 0},
{0, 0, 2, 2},
{0, 0, 2, 0}
};
int result = minDistanceOfBlocks(grid); // 忽略值为0
cout << "连通块最小距离: " << result << endl; // 输出3
return 0;
}
代码说明
- BFS 识别连通块:
bfs
函数通过广度优先搜索遍历所有四连通的相同元素,记录其坐标并标记所属连通块。 - 块间距离计算:
minDistanceBetweenTwoBlocks
函数计算两个连通块中所有点对的曼哈顿距离(公式:∣x1−x2∣+∣y1−y2∣|x_1 - x_2| + |y_1 - y_2|∣x1−x2∣+∣y1−y2∣),返回最小值。 - 全局最小距离:
minDistanceOfBlocks
函数先识别所有目标连通块(排除忽略值,如 0),再遍历所有块对计算最小距离。
示例解析
对于网格:
1 1 0 0
1 0 0 0
0 0 2 2
0 0 2 0
- 连通块 0(值为1)包含坐标:(0,0),(0,1),(1,0)(0,0), (0,1), (1,0)(0,0),(0,1),(1,0)。
- 连通块 1(值为2)包含坐标:(2,2),(2,3),(3,2)(2,2), (2,3), (3,2)(2,2),(2,3),(3,2)。
- 块间最小距离为 3(如 (0,1)(0,1)(0,1) 到 (2,2)(2,2)(2,2) 的曼哈顿距离:∣0−2∣+∣1−2∣=2+1=3|0-2| + |1-2| = 2 + 1 = 3∣0−2∣+∣1−2∣=2+1=3)。
扩展说明
- 若需八连通,修改
dirs
为{{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}}
。 - 若需欧氏距离,将距离计算改为 (x1−x2)2+(y1−y2)2\sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}(x1−x2)2+(y1−y2)2(代码中对应
sqrt(pow(p1.first-p2.first, 2) + pow(p1.second-p2.second, 2))
)。 - 大规模网格可优化:仅计算连通块的边界点(内部点距离不会比边界点更近),减少计算量。