每日一题:最大加号标志

在一个 n x n 的矩阵 grid 中,除了在数组 mines 中给出的元素为 0,其他每个元素都为 1mines[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) 都 不重复​​​​​​​

最简单的思路就是暴力遍历,对每个各自向上、向下、向左、向右延伸,记录下各自能到达的最远距离,然后取最小值就是能构成的最大加号。

在这个过程中,是否有一些可以利用到的信息?去减少遍历的次数?

以向右延伸为例,grid[0][0]向右延伸的最大值取决于其本身的值(是否为0),和grid[0][1]向右延伸的最大值,即在向右的这个维度上:

grid[i][j] = \left\{\begin{matrix} 0& ,grid[i][j] = 0 & \\ grid[i][j+1] + 1& ,grid[i][j] !=0 & \end{matrix}\right.

到这里显然就是用到动态规划的思路。

总共有4个维度,上下左右,所以考虑设计一种遍历方式,可以高效率的同时计算出各个维度上的结果,我们考虑利用矩阵的对称性,如下图所示(可以先下去看代码回头再看图解):

i,j为常规遍历时的当前格子,正常需要从i,j向四个方向延伸。

这里多定义了一个变量k,它从末尾开始递减,它的作用是:随着j的增加,k是减少的,如果将j的增加描述为向右延伸,那么k的减少就是向左延伸

同时,由于矩阵对称性(i,j和j,i关于对角线),i,j描述向右延伸,那么j,i就能描述向下延伸。(这里由于是方阵,所以不用考虑越界问题)

按照这种遍历方式,移动一次(注意箭头方向描述延伸方向):

 移动两次,此时j = k,但是描述的方向不同:

移动第三次,这里的格子和第一次移动一样,但是延伸方向相反:

移动第四次,对i = 0的情况遍历完成:

 在这个过程中我们得到了什么?

对于[0][0]这个格子,我们得到了四个方向的延伸值,而对于其他经过的格子,有的得到了左右延伸的值(第一行),有的得到了上下延伸的值(第一列)。

接下来对i加1,重复这个过程,注意箭头方向和上面的不同,看到这里对于第一行,我们将给他补上纵向也就是上下方向的延伸结果:

 当所有i遍历完成,我们就得到了dp矩阵,而结果就是dp矩阵的最大值。

完整代码:

class Solution {
public:
    int orderOfLargestPlusSign(int n, vector<vector<int>>& mines) {
        vector<vector<int>> dp(n,vector<int>(n,n));
        int result = 0;
        for(auto& item : mines){
            dp[item[0]][item[1]] = 0;
        }
        for(int i = 0;i < n;++i){
            int left = 0;
            int right = 0;
            int up = 0;
            int down = 0;
            for(int j = 0,k = n-1;j < n;++j,--k){
                left = dp[i][j] == 0 ? 0 : left + 1;
                right = dp[i][k] == 0 ? 0 : right + 1;
                up = dp[j][i] == 0 ? 0 : up + 1;
                down = dp[k][i] == 0 ? 0 : down + 1;
                dp[i][j] = min(dp[i][j],left);
                dp[i][k] = min(dp[i][k],right);
                dp[j][i] = min(dp[j][i],up);
                dp[k][i] = min(dp[k][i],down);
            }
        }
        for(auto& item : dp){
            result = max(result,*max_element(item.begin(),item.end()));
        }
        return result;
    }
};

 针对上面的代码:

  1. vector<vector<int>> dp(n,vector<int>(n,n));这里将每个值初始化为n,原因是后续要取最小值,并且最长的加号长度也就是n。
  2. max_element(item.begin(),item.end())将返回指向最大值的迭代器,*max_element才是最大值。
  • 114
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值