网格中连通块的最小距离求解

网格中连通块的最小距离求解

问题定义

在二维网格中,连通块指由相邻(通常为四连通:上下左右) 的相同元素组成的区域。求解连通块的最小距离,即找到两个不同连通块中任意两点之间的最短距离(常用曼哈顿距离或欧氏距离定义)。

核心思路

  1. 识别连通块:遍历网格,通过 BFS 或 DFS 标记所有连通块,记录每个连通块包含的所有坐标。
  2. 计算块间距离:对每对不同的连通块,计算其内部所有点对的距离,取最小值作为这两个块的距离。
  3. 全局最小距离:在所有块间距离中取最小值,即为结果。

关键细节

  • 连通性:默认四连通(上下左右),如需八连通(含对角线)可调整方向数组。
  • 距离定义
    • 曼哈顿距离:∣x1−x2∣+∣y1−y2∣|x_1 - x_2| + |y_1 - y_2|x1x2+y1y2(网格中移动步数,常用)。
    • 欧氏距离:(x1−x2)2+(y1−y2)2\sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}(x1x2)2+(y1y2)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;
}

代码说明

  1. BFS 识别连通块bfs 函数通过广度优先搜索遍历所有四连通的相同元素,记录其坐标并标记所属连通块。
  2. 块间距离计算minDistanceBetweenTwoBlocks 函数计算两个连通块中所有点对的曼哈顿距离(公式:∣x1−x2∣+∣y1−y2∣|x_1 - x_2| + |y_1 - y_2|x1x2+y1y2),返回最小值。
  3. 全局最小距离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∣02∣+∣12∣=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}(x1x2)2+(y1y2)2(代码中对应 sqrt(pow(p1.first-p2.first, 2) + pow(p1.second-p2.second, 2)))。
  • 大规模网格可优化:仅计算连通块的边界点(内部点距离不会比边界点更近),减少计算量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值