【程序员面试金典】17.23. 最大黑方阵(动态规划)

1.题目

给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出 4 条边皆为黑色像素的最大子方阵。

返回一个数组 [r, c, size] ,其中 r, c 分别代表子方阵左上角的行号和列号,size 是子方阵的边长。若有多个满足条件的子方阵,返回 r 最小的,若 r 相同,返回 c 最小的子方阵。若无满足条件的子方阵,返回空数组。

示例 1:

输入:
[
   [1,0,1],
   [0,0,1],
   [0,0,1]
]
输出: [1,0,2]
解释: 输入中 0 代表黑色,1 代表白色,标粗的元素即为满足条件的最大子方阵
示例 2:

输入:
[
   [0,1,1],
   [1,0,1],
   [1,1,0]
]
输出: [0,0,1]
提示:

matrix.length == matrix[0].length <= 200

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-black-square-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.题解

从矩阵右下角开始逐层向上更新每个元素向下和向右的连续1的个数。(动态规划)
过程中记录最大的方形边长和元素位置。

class Solution {
public:
    vector<int> findSquare(vector<vector<int>>& matrix) {
        vector<int> ans(3, 0);
        int n = matrix.size();
        if(n == 0) return {};
        if(n == 1){
            if(matrix[0][0] == 0)
                return {0, 0, 1};
            else
                return {};
        }
        //cnt[r][c][0/1],0右侧,1下侧
        vector<vector<vector<int>>> cnt(n, vector<vector<int>>(n, vector<int>(2)));
        for(int r = n-1; r >= 0; r--){
            for(int c = n-1; c >= 0; c--){
                if(matrix[r][c] == 1)
                    cnt[r][c][0] = cnt[r][c][1] = 0;
                else{
                    //统计cnt[r][c][0/1]
                    if(r < n-1) cnt[r][c][1] = cnt[r+1][c][1] + 1;
                    else cnt[r][c][1] = 1;

                    if(c < n-1) cnt[r][c][0] = cnt[r][c+1][0] + 1;
                    else cnt[r][c][0] = 1;
				   //更新当前最大子方阵
                    int len = min(cnt[r][c][0], cnt[r][c][1]);//最大的可能的边长
                    while(len >= ans[2]){//要答案r,c最小,所以带等号
                        if(cnt[r+len-1][c][0] >= len && cnt[r][c+len-1][1] >= len){
                            //可以构成长为len的方阵
                            ans = {r, c, len};
                            break;
                        }
                        len--;
                    }
                }
            }
        }
        return ans;
    }
};

链接:https://leetcode-cn.com/problems/max-black-square-lcci/solution/c-dong-tai-gui-hua-by-tmoonli/
下面这个效率更高,更节省空间。(思路都一样)

class Solution {
public:
    vector<int> findSquare(vector<vector<int>>& matrix) {
        int R = matrix.size();
        if(R == 0) return {};
        
        // 当前位置向左连续0的个数
        int fL[R][R];
        memset(fL, 0x00, sizeof fL);
        // 当前位置向上连续0的个数
        int fU[R][R];
        memset(fU, 0x00, sizeof fU);
        // 初始化
        for(int i=0; i<R; ++i){
            for(int j=0; j<R; ++j){
                if(matrix[i][j]==0){
                    if(i==0 && j==0){
                        fU[i][j]=1;
                        fL[i][j]=1;
                    }
                    else if(i==0 && j>0){
                        fU[i][j] = 1;
                        fL[i][j] = fL[i][j-1]+1;
                    }
                    else if(j==0 && i>0){
                        fL[i][j] = 1;
                        fU[i][j] = fU[i-1][j]+1;
                    }
                    else if(i>0 && j>0){
                        fL[i][j] = fL[i][j-1]+1;
                        fU[i][j] = fU[i-1][j]+1;
                    }
                }   
            }
        }

        vector<int> res;
        for(int i=R-1; i>=0; --i){
            for(int j=R-1; j>=0; --j){
                int L = min(fL[i][j], fU[i][j]);
                for(int k=L; k>0; --k){
                    if(res.size()>0 && k<res[2]) break;
                    // 左上角顶点
                    int px = i-k+1;
                    int py = j-k+1;
                    // 检测另外两条边能否构成正方形
                    if(fL[px][j]>=k && fU[i][py]>=k){
                        if(res.size()==0 || px<res[0] || py<res[1])
                            res = {px, py, k};
                    }
                }
            }
        }

        return res;
    }
};

其实if(res.size()==0 || px<res[0] || py<res[1])这行代码可以删除,因为题目要求,若有多个满足条件的子方阵,返回 r 最小的,若 r 相同,返回 c 最小的子方阵。
我们是按照相反方向的进行遍历,只要进行更新,就是符合题目要求的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值