难度中等126
在一个 n x n
的矩阵 grid
中,除了在数组 mines
中给出的元素为 0
,其他每个元素都为 1
。mines[i] = [xi, yi]
表示 grid[xi][yi] == 0
返回 grid
中包含 1
的最大的 轴对齐 加号标志的阶数 。如果未找到加号标志,则返回 0
。
一个 k
阶由 1
组成的 “轴对称”加号标志 具有中心网格 grid[r][c] == 1
,以及4个从中心向上、向下、向左、向右延伸,长度为 k-1
,由 1
组成的臂。注意,只有加号标志的所有网格要求为 1
,别的网格可能为 0
也可能为 1
。
示例 1:
输入: n = 5, mines = [[4, 2]] 输出: 2 解释: 在上面的网格中,最大加号标志的阶只能是2。一个标志已在图中标出。
示例 2:
输入: n = 1, mines = [[0, 0]] 输出: 0 解释: 没有加号标志,返回 0 。
提示:
1 <= n <= 500
1 <= mines.length <= 5000
0 <= xi, yi < n
- 每一对
(xi, yi)
都 不重复
分析:本题无非就是要求以某个点(x,y)为中心,向上下左右延申得到的四个长度中的最小值。
思路1:枚举
我们可以直接枚举n*n个点,然后再进行四个方向上的长度探索,复杂度为O(n*n*4n)=O(n^3)。
思路2:枚举优化-值维护
如果我们可以遍历一遍所有的点就知道每一个点上下左右的长度,就可以直接得到每个点位置的加号标志长度了。这可能吗?我们来试一下。假设我们现在有一个数组left_len[i][j],代表着以(i,j)为中心,向左延伸的最大长度,那么我们容易推知
那么类似的可以得到
只需要两次遍历,从左上向右下遍历可以得到left_len与up_len,从右下往左上遍历可以得到right_len与buttom_len,那么可以推知:
class Solution {
public:
int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
vector<vector<int>> grid = vector(n, vector<int>(n, 1));
vector<vector<int>> left_len = vector(n, vector<int>(n, 0));
vector<vector<int>> right_len = vector(n, vector<int>(n, 0));
vector<vector<int>> up_len= vector(n, vector<int>(n, 0));
vector<vector<int>> bottom_len = vector(n, vector<int>(n, 0));
for(vector mine:mines){
grid[mine[0]][mine[1]] = 0;
}
int i, j, max_len = n * n == mines.size() ? 0 : 1;
for(i = 0; i < n; ++ i){
up_len[0][i] = grid[0][i];
left_len[i][0] = grid[i][0];
if(i > 0){
up_len[i][0] = grid[i][0] == 1 ? 1 + up_len[i - 1][0] : 0;
left_len[0][i] = grid[0][i] == 1 ? 1 + left_len[0][i - 1] : 0;
}
}
for(i = 1; i < n; ++ i){
for(j = 1; j < n; ++ j){
up_len[i][j] = grid[i][j] == 0 ? 0 : up_len[i - 1][j] + 1;
left_len[i][j] = grid[i][j] == 0 ? 0 : left_len[i][j - 1] + 1;
}
}
for(i = n - 1; i >= 0; -- i){
bottom_len[n - 1][i] = grid[n - 1][i];
right_len[i][n - 1] = grid[i][n - 1];
if(i < n - 1){
bottom_len[i][n - 1] = grid[i][n - 1] == 1 ? 1 + bottom_len[i + 1][n - 1] : 0;
right_len[n - 1][i] = grid[n - 1][i] == 1 ? 1 + right_len[n - 1][i + 1] : 0;
}
}
for(i = n - 2; i >= 1; -- i){
for(j = n - 2; j >= 1; -- j){
bottom_len[i][j] = grid[i][j] == 0 ? 0 : bottom_len[i + 1][j] + 1;
right_len[i][j] = grid[i][j] == 0 ? 0 : right_len[i][j + 1] + 1;
max_len = max(max_len, min(min(up_len[i][j], left_len[i][j]), min(bottom_len[i][j], right_len[i][j])));
}
}
return max_len;
}
};
思路3:空间节约
我们需要像思路2一样另外存储四个数组吗?倒不见得。我们知道上面之所以不用一个数组来存储是因为害怕对后续的更新有影响。举个例子:
如上图所示,grid[2][1]=1,且这个位置的最大长度为1即len[2][1]=1。而现在我们考虑(2,2),理想情况下,我们应该从他的四个邻接位置中取一个最小值+1,就得到答案了,然而实际上我们不能这样做,以上面的为例,我们取左邻接(2,1)的长度为1,其他三个位置都是2,然而实际的答案是3并不是1+1=2。原因在于我们本想借助(2,1)来获取(2,2)的最大左边长度,然后实际上len[2][1]给我们的结果却是(2,1)考虑了(1,1)位置的0的答案。
所以推知,我们没有办法在上一步已经考虑了四条边(其实我们最多一次性智能考虑两条边、左上-右下,左下-右上都可)的情况下得到正确答案。而正确的做法是将上下左右的遍历单独运算。
class Solution {
public:
void printf_mat(vector<vector<int>>& dp, int n){
for(int i = 0; i < n; ++ i){
for(int j = 0; j < n; ++ j){
printf("%d ", dp[i][j]);
}
printf("\n");
}
}
int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
vector<vector<int>> dp = vector(n, vector<int>(n, n));
unordered_set<int> banned_idx;
for(vector<int> mine:mines){
banned_idx.emplace(mine[0] * n + mine[1]);
}
// from left to right
//printf("l -> r:\n");
int continue_one_num, i, j, max_len = 0;
for(i = 0; i < n; ++ i){
continue_one_num = 0;
for(j = 0; j < n; ++ j){
if(banned_idx.find(i * n + j) == banned_idx.end()){
++ continue_one_num;
}else{
continue_one_num = 0;
}
dp[i][j] = min(dp[i][j], continue_one_num);
}
}
//printf_mat(dp, n);
// from up to buttom
//printf("u -> b:\n");
continue_one_num = 0;
for(i = 0; i < n; ++ i){
continue_one_num = 0;
for(j = 0; j < n; ++ j){
if(banned_idx.find(j * n + i) == banned_idx.end()){
++ continue_one_num;
}else{
continue_one_num = 0;
}
dp[j][i] = min(dp[j][i], continue_one_num);
}
}
//printf_mat(dp, n);
// from right to left
//printf("r -> l:\n");
continue_one_num = 0;
for(i = 0; i < n; ++ i){
continue_one_num = 0;
for(j = n - 1; j >= 0; --j){
if(banned_idx.find(i * n + j) == banned_idx.end()){
++ continue_one_num;
}else{
continue_one_num = 0;
}
dp[i][j] = min(dp[i][j], continue_one_num);
}
}
//printf_mat(dp, n);
// from buttom to up
//printf("b -> u:\n");
continue_one_num = 0;
for(i = 0; i < n; ++ i){
continue_one_num = 0;
for(j = n - 1; j >= 0; -- j){
if(banned_idx.find(j * n + i) == banned_idx.end()){
++ continue_one_num;
}else{
continue_one_num = 0;
}
dp[j][i] = min(dp[j][i], continue_one_num);
max_len = max(max_len, dp[j][i]);
}
}
//printf_mat(dp, n);
return max_len;
}
};