题目地址:
https://leetcode.com/problems/find-the-safest-path-in-a-grid/description/
给定一个 n × n n\times n n×n的 01 01 01矩阵, 1 1 1代表贼, 0 0 0代表空地,每个位置的安全系数为其和与其曼哈顿距离最近的贼的曼哈顿距离。现在要从 ( 0 , 0 ) (0,0) (0,0)走到 ( n − 1 , n − 1 ) (n-1,n-1) (n−1,n−1),每一步只能走 4 4 4个方向,不能出界。一条路径的安全系数定义为其包含的所有点的安全系数的最小值。问安全系数最大的路径的安全系数。
思路是多源BFS+最小生成树Kruskal算法。先用多源BFS求出每个位置的安全系数,然后再用Kruskal算法。设安全系数矩阵为 c c c,那么问题就变为在 c c c里的所有从左上到右下的路径中,路径上最小值最大的那条路径的路径点最小值。可以将相邻点连边,那么经过这条边的安全系数就是两个点的安全系数的较小值。我们可以按安全系数从高到低对所有边排序,然后依次将边加上去(按照Kruskal算法的步骤),一旦加了某条边能使得左上和右下连通,那么这条边的安全系数就是答案。代码如下:
class Solution {
public:
using PII = pair<int, int>;
struct Edge {
int x, y, w;
};
vector<int> p;
int maximumSafenessFactor(vector<vector<int>>& g) {
int n = g.size(), nn = n * n;
p.resize(nn);
for (int i = 0; i < nn; i++) p[i] = i;
queue<PII> q;
vector<vector<int>> s(n, vector<int>(n, -1));
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (g[i][j]) {
q.push({i, j});
s[i][j] = 0;
}
static int d[] = {-1, 0, 1, 0, -1};
while (q.size()) {
auto t = q.front();
q.pop();
int x = t.first, y = t.second;
for (int k = 0; k < 4; k++) {
int nx = x + d[k], ny = y + d[k + 1];
if (0 <= nx && nx < n && 0 <= ny && ny < n) {
if (~s[nx][ny]) continue;
s[nx][ny] = s[x][y] + 1;
q.push({nx, ny});
}
}
}
auto f = [&](int x, int y) { return x * n + y; };
vector<Edge> es;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
if (i + 1 < n)
es.push_back({f(i, j), f(i + 1, j), min(s[i][j], s[i + 1][j])});
if (j + 1 < n)
es.push_back({f(i, j), f(i, j + 1), min(s[i][j], s[i][j + 1])});
}
sort(es.begin(), es.end(), [&](auto& e1, auto& e2) { return e1.w > e2.w; });
auto merge = [&](int x, int y) {
int px = find(x), py = find(y);
p[px] = py;
};
for (auto& e : es) {
int x = e.x, y = e.y, w = e.w;
merge(x, y);
if (find(0) == find(nn - 1)) return w;
}
return 0;
}
int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
};
时间复杂度 O ( n 2 log n ) O(n^2\log n) O(n2logn),空间 O ( n 2 ) O(n^2) O(n2)。