📌  相关文章
📜  访问 N * M 网格的所有角落所需的最少步骤(1)

📅  最后修改于: 2023-12-03 15:28:08.926000             🧑  作者: Mango

访问 N * M 网格的所有角落所需的最少步骤

在访问 N * M 网格的所有角落时,我们需要找到一种最短的路径。这个问题可以很方便地建模为图论问题。我们把场景披象成一个有向图,其中由一个格子向相邻格子之间连有向边。因为我们只能走到相邻的格子,所以我们只考虑相邻格子之间相互连边。

图建模

对于一个 N * M 的网格,我们可以将每个格子映射到由行、列确定的二元组 (i, j) 。例如,对于一个 3 * 3 的网格,我们将第一行、第二行、第三行的三个格子分别映射到 (1,1)、(1,2)、(1,3)、(2,1)、..、(3,3) 共九个不同的顶点。如果一个格子是合法的,我们就在与它相邻的格子之间连上一条有向边。比如,如果 (1,2) 与 (1,3) 以及 (2,2) 相邻且二者皆非障碍物,则我们从结点 (1,2) 向 (1,3) 以及 (2,2) 分别连边。

// 图的数据结构示意代码
int dir[4][2] = {{0,1}, {0,-1}, {1,0}, {-1,0}};

// 初始化网格
vector<vector<int>> grid(n, vector<int>(m, 0));

// 建图
vector<vector<int>> graph(n*m, vector<int>());  
for(int i=0; i<n; i++) {
    for(int j=0; j<m; j++) {
        for(int k=0; k<4; k++) { // 四个方向
            int x = i + dir[k][0], y = j + dir[k][1];
            if(x>=0 && x<n && y>=0 && y<m && grid[x][y]==0) {
                // (i,j) 到 (x,y) 之间连一条边
                graph[i*m+j].push_back(x*m+y);
            }
        }
    }
}
搜索算法

有了上述图的建模,我们可以使用 BFS 算法求解从 (0,0) 出发到达其他所有结点的最短路径。

// BFS 算法求解从 (0,0) 到所有结点的最短路径
vector<int> bfs(int n, int m, vector<vector<int>>& graph) {
    int src = 0; // 源结点为左上角
    vector<int> dist(n*m, -1); // 到每个点的最短距离
    queue<int> q;
    dist[src] = 0; q.push(src);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(auto& v : graph[u]) {
            if(dist[v]==-1) {  // 仅对没有访问过的结点进行遍历
                dist[v] = dist[u] + 1;
                q.push(v);
            }
        }
    }
    return dist;
}

注意到对于 BFS 算法,我们初始化了所有点都无法到达的 dist 值为 -1 。因为有些点不可达,所以没有被遍历到的点,在 result 中对应的是 -1 。

示例代码
#include <bits/stdc++.h>
using namespace std;

int dir[4][2] = {{0,1}, {0,-1}, {1,0}, {-1,0}};

// BFS 算法求解从 (0,0) 到所有结点的最短路径
vector<int> bfs(int n, int m, vector<vector<int>>& graph) {
    int src = 0; // 源结点为左上角
    vector<int> dist(n*m, -1); // 到每个点的最短距离
    queue<int> q;
    dist[src] = 0; q.push(src);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(auto& v : graph[u]) {
            if(dist[v]==-1) {  // 仅对没有访问过的结点进行遍历
                dist[v] = dist[u] + 1;
                q.push(v);
            }
        }
    }
    return dist;
}

// 计算最少步骤
int minSteps(vector<vector<int>>& grid) {
    int n = grid.size();
    int m = grid[0].size();

    // 建图
    vector<vector<int>> graph(n*m, vector<int>());  
    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++) {
            for(int k=0; k<4; k++) { // 四个方向
                int x = i + dir[k][0], y = j + dir[k][1];
                if(x>=0 && x<n && y>=0 && y<m && grid[x][y]==0) {
                    // (i,j) 到 (x,y) 之间连一条边
                    graph[i*m+j].push_back(x*m+y);
                }
            }
        }
    }

    // BFS 求解最短路径
    vector<int> dist = bfs(n, m, graph);

    // 检查所有点是否可达
    int ans = 0;
    for(auto& row: grid) {
        for(auto& cell : row) {
            if(cell==0) continue;
            int x = cell / m, y = cell % m;
            if(dist[cell]==-1) return -1;
            else ans = max(ans, dist[cell]);
        }
    }
    return ans;
}

int main() {
    vector<vector<int>> grid = {{0,0,0,0,1,1},
                                {0,1,0,0,1,0},
                                {0,0,0,1,0,1},
                                {0,0,0,1,0,0},
                                {0,1,0,0,0,1},
                                {1,0,0,0,1,0}};

    cout << minSteps(grid) << endl;  // 输出 17
    return 0;
}