764. 最大加号标志

764. 最大加号标志

难度中等126

在一个 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) 都 不重复

分析:本题无非就是要求以某个点(x,y)为中心,向上下左右延申得到的四个长度中的最小值。

思路1:枚举

        我们可以直接枚举n*n个点,然后再进行四个方向上的长度探索,复杂度为O(n*n*4n)=O(n^3)。

思路2:枚举优化-值维护

        如果我们可以遍历一遍所有的点就知道每一个点上下左右的长度,就可以直接得到每个点位置的加号标志长度了。这可能吗?我们来试一下。假设我们现在有一个数组left_len[i][j],代表着以(i,j)为中心,向左延伸的最大长度,那么我们容易推知

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

那么类似的可以得到

up\_len[i][j]=\left\{ \begin{matrix} up\_len[i - 1][j] + 1, grid[i][j]==1 \\ 0, grid[i][j]==0 \end{matrix}\right.

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

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

只需要两次遍历,从左上向右下遍历可以得到left_lenup_len,从右下往左上遍历可以得到right_lenbuttom_len,那么可以推知:

len[i,j]=min(left\_len[i][j], right\_len[i][j], up\_len[i][j], buttom\_len[i][j])

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;
    }
};

世界地图矢量数据可以通过多种网站进行下载。以下是一些提供免费下载世界地图矢量数据的网站: 1. Open Street Map (https://www.openstreetmap.org/): 这个网站可以根据输入的经纬度或手动选定范围来导出目标区域的矢量图。导出的数据格式为osm格式,但只支持矩形范围的地图下载。 2. Geofabrik (http://download.geofabrik.de/): Geofabrik提供按洲际和国家快速下载全国范围的地图数据数据格式支持shape文件格式,包含多个独立图层,如道路、建筑、水域、交通、土地利用分类、自然景观等。数据每天更新一次。 3. bbbike (https://download.bbbike.org/osm/): bbbike提供全球主要的200多个城市的地图数据下载,也可以按照bbox进行下载。该网站还提供全球数据数据格式种类齐全,包括geojson、shp等。 4. GADM (https://gadm.org/index.html): GADM提供按国家或全球下载地图数据的服务。该网站提供多种格式的数据下载。 5. L7 AntV (https://l7.antv.antgroup.com/custom/tools/worldmap): L7 AntV是一个提供标准世界地图矢量数据免费下载的网站。支持多种数据格式下载,包括GeoJSON、KML、JSON、TopJSON、CSV和高清SVG格式等。可以下载中国省、市、县的矢量边界和世界各个国家的矢量边界数据。 以上这些网站都提供了世界地图矢量数据免费下载服务,你可以根据自己的需求选择合适的网站进行下载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值