Leetcode1139 最大以1为边界的正方形(预处理数组技巧)

方法1:左神的暴力法和预处里数组检查法

class Solution {
    //数组预处理法,大幅度加快速度
    public int largest1BorderedSquare(int[][] grid) {
        if(grid.length==0||grid[0].length==0){
            return 0;
        }
        int[][] right=new int[grid.length][grid[0].length];
        int[][] down=new int[grid.length][grid[0].length];
        initPA(right,down,grid);
        int chang=grid[0].length;
        int height=grid.length;
        for(int k=Math.min(grid.length,grid[0].length);k>0;k--){//从最大边开始找,找到就返回k*k
            for(int i=0;i+k-1<height;i++){
                for(int j=0;j+k-1<chang;j++){
                    //检查边界是否都是1;
                    if(check(i,j,k,right,down)){
                        return k*k;
                    }
                }
            }
        }
        return 0;
    }
    public boolean check(int i,int j,int k,int[][] right,int[][] down){
        if(right[i][j]>=k&&down[i][j]>=k&&right[i+k-1][j]>=k&&down[i][j+k-1]>=k){
            return true;
        }
        return false;
    }
    public void initPA(int[][] right,int[][] down,int[][] grid){ //init prepare array
        //初始化right数组,从第一行最后边开始初始化
        for(int i=0;i<right.length;i++){
            for(int j=right[0].length-1;j>=0;j--){
                if(j==right[0].length-1&&grid[i][j]==1){
                    right[i][j]=1;
                }else if(j!=right[0].length-1&&grid[i][j]==1){
                    right[i][j]=right[i][j+1]+1;
                }
            }
        }
        //初始化down数组
        for(int j=0;j<down[0].length;j++){
            for(int i=down.length-1;i>=0;i--){
                if(i==down.length-1&&grid[i][j]==1){
                    down[i][j]=1;
                }else if(i!=down.length-1&&grid[i][j]==1){
                    down[i][j]=down[i+1][j]+1;
                }
            }
        }
    }
    
    
    
    
    //暴力检查法
    /**
    *有个注意的地方,就是边长,如只有1.这也是正方形,表示边长为1的正方形,所以for里面是i+k-1,因为这样才是边长
    */
    
    public int largest1BorderedSquare1(int[][] grid) {
        if(grid.length==0||grid[0].length==0){
            return 0;
        }
        int chang=grid[0].length;
        int height=grid.length;
        for(int k=Math.min(grid.length,grid[0].length);k>0;k--){//从最大边开始找,找到就返回k*k
            for(int i=0;i+k-1<height;i++){
                for(int j=0;j+k-1<chang;j++){
                    //检查边界是否都是1;
                    if(check1(i,j,k,grid)){
                        return k*k;
                    }
                }
            }
        }
        return 0;
    }
    public boolean check1(int i,int j,int k,int[][] grid){
        //一共检查四条边,第一条是第一横边,第二横边,第1戍边,第二戍边
        //但是可以发现,两个横边可以放在一起检查,两个戍边也可以放在一起检查,所以只需要两个for循环
        
        //首先检查横边
        for(int m=j;m<=j+k-1;m++){
            if(grid[i][m]!=1||grid[i+k-1][m]!=1){
                return false;
            }
        }
        //检查戍边
        for(int m=i;m<=i+k-1;m++){
            if(grid[m][j]!=1||grid[m][j+k-1]!=1){
                return false;
            }
        }
        return true;
    }
}

还有,例如求一个数组0-i位置的最大值,你可以这样:就是新建一个数组,然后遍历原数组,从头遍历,如果当前数小于前一个,就记录前一个,这样:
1 2 0 2 3 0 5-》
1 2 2 2 3 3 5 就这个意思,我的表达能力有问题。。。表达不清楚

方法2:leetcode大神方法,我搞不懂,暴力法是29秒,优化之后是13秒,但是这个和优化的时间复杂度只差在常数项,但是他是5秒,这个快的很多
没有去操作呢,回头看

class Solution {
    public int largest1BorderedSquare(int[][] grid) {
        if (grid.length==0) return 0;
        int[][] dpr = new int[grid.length+1][grid[0].length+1];
        int[][] dpc = new int[grid.length+1][grid[0].length+1];
        int dist, max=0;
        for (int r=1;r<=grid.length;r++){
            for (int c=1;c<=grid[0].length;c++){
                if (grid[r-1][c-1]==0) continue;
                dpr[r][c] = dpr[r-1][c]+1;
                dpc[r][c] = dpc[r][c-1]+1;
                dist = Math.min(dpr[r][c],dpc[r][c]);
                for (int i=dist;i>=1;i--){
                    **if (dpr[r][c-i+1]>=i** 
                        && dpc[r-i+1][c]>=i){
                        max = Math.max(max, i*i);
                        break;
                    }
                }
            }
        }
        return max;
    }
}

补丁:用的是leetcode大神思想做的,除了数组基准不同,其他的都一样,因为习惯我喜欢用原数组作为基准,他的思想就是从头到尾遍历数组,每一个点当做的是右下角的顶点,他有个处理非常好,就是left和up数组比grid数组要大一些,这样就不用初始化两条边了,代码简洁了,牛逼

class Solution {
    public int largest1BorderedSquare(int[][] grid) {
        if(grid.length==0||grid[0].length==0){
            return 0;
        }
        //他找的是右下角的点,以前我的做法是左上角,不一样的
        int[][] left=new int[grid.length+1][grid[0].length+1];
        int[][] up=new int[grid.length+1][grid[0].length+1];
        int max=0;
        //这个比grid大了一些,但是就是这些,使得后续的处理方便了,因为他相当于初始化了第一行和第一列,否则,还要
        //分支处理,加判断
        //leetcode大神,是用的left和up当基准的(更方便但是理解性差一点,所以我才用的是grid当做标准的)
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                //每次到一个位置,先更新left和up数组
                if(grid[i][j]==0){
                    continue;//当前为0的不做任何判断,也不作任何处理
                }
                left[i+1][j+1]=left[i+1][j]+1;
                up[i+1][j+1]=up[i][j+1]+1;
                int k=Math.min(left[i+1][j+1],up[i+1][j+1]);//以当前为右下角顶点最大边长。主要是减少比较次数的,要不然你岂不是遍历很多次
                for(int dist=k;dist>0;dist--){
                    if(up[i+1][j+1-dist+1]>=dist&&left[i+1-dist+1][j+1]>=dist){
                        max=Math.max(max,dist);
                        break;
                    }
                }
            }
        }
        return max*max;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述:给定一个非负整数数组nums和一个整数m,你需要将这个数组分成m个非空的连续子数组。设计一个算法使得这m个子数组中的最大和最小。 解题思路: 这是一个典型的二分搜索题目,可以使用二分查找来解决。 1. 首先确定二分的左右边界。左边界数组最大的值,右边界数组中所有元素之和。 2. 在二分搜索的过程中,计算出分割数组的组数count,需要使用当前的中间值来进行判断。若当前的中间值不够分割成m个子数组,则说明mid值偏小,将左边界更新为mid+1;否则,说明mid值偏大,将右边界更新为mid。 3. 当左边界小于等于右边界时,循环终止,此时的左边界即为所求的结果。 具体步骤: 1. 遍历数组,找到数组中的最大值,并计算数组的总和。 2. 利用二分查找搜索左右边界,从左边界到右边界中间的值为mid。 3. 判断当前的mid值是否满足题目要求,若满足则更新右边界为mid-1; 4. 否则,更新左边界为mid+1。 5. 当左边界大于右边界时,循环终止,返回左边界即为所求的结果。 代码实现: ```python class Solution: def splitArray(self, nums: List[int], m: int) -> int: left = max(nums) right = sum(nums) while left <= right: mid = (left + right) // 2 count = 1 total = 0 for num in nums: total += num if total > mid: total = num count += 1 if count > m: left = mid + 1 else: right = mid - 1 return left ``` 时间复杂度分析:二分搜索的时间复杂度为O(logN),其中N为数组的总和,而遍历数组的时间复杂度为O(N),因此总的时间复杂度为O(NlogN)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值