算法题 区间加法 II

598. 区间加法 II

问题描述

给你一个 m x n 的矩阵 M,初始时全部元素为 0。在 M 上执行 k 次操作,其中每个操作用一个长度为 2 的正整数数组 ops[i] = [ai, bi] 表示。

在第 i 次操作中,对所有满足 0 ≤ r < ai0 ≤ c < bi 的元素 M[r][c] 加 1。

请你返回 k 次操作后,M 中最大整数的元素个数。

示例

输入: m = 3, n = 3, ops = [[2,2],[3,3]]
输出: 4
解释: 
初始矩阵: [[0,0,0],[0,0,0],[0,0,0]]
操作[2,2]: [[1,1,0],[1,1,0],[0,0,0]]
操作[3,3]: [[2,2,1],[2,2,1],[1,1,1]]
最大整数是2,出现了4次。

算法思路

核心

  1. 操作特性:每次操作都是对左上角矩形区域 [0,0][ai-1,bi-1] 加 1
  2. 关键:被所有操作覆盖的区域,其值最大
  3. 交集思想:找所有操作矩形的最大公共区域

贪心策略

  • 每次操作都会影响左上角的一个矩形区域
  • 被操作次数最多的区域 = 被所有操作都覆盖的区域
  • 这个区域是所有操作矩形的交集

数学分析

设:

  • minRow = min(ops[i][0] 到 ops[m][0])
  • minCol = min(ops[i][1] 到 ops[m][1])

则最大值区域为左上角的 minRow × minCol 矩形,且该区域的值等于操作次数 k

为什么?

  • 任何超出 minRowminCol 的位置,至少被一个操作遗漏
  • 只有 [0, minRow) × [0, minCol) 区域被所有操作覆盖

代码实现

class Solution {
    /**
     * 计算区间加法后矩阵中最大整数的元素个数
     * 
     * @param m 矩阵行数
     * @param n 矩阵列数  
     * @param ops 操作数组,每个操作[ai, bi]表示对[0,ai)×[0,bi)区域加1
     * @return 最大整数的元素个数
     */
    public int maxCount(int m, int n, int[][] ops) {
        // 边界情况:如果没有操作,所有元素都是0
        if (ops.length == 0) {
            return m * n;
        }
        
        // 找到所有操作中行范围的最小值
        int minRow = Integer.MAX_VALUE;
        // 找到所有操作中列范围的最小值
        int minCol = Integer.MAX_VALUE;
        
        // 遍历所有操作
        for (int[] op : ops) {
            int rows = op[0];  // 当前操作影响的行数
            int cols = op[1];  // 当前操作影响的列数
            
            // 更新最小行范围
            minRow = Math.min(minRow, rows);
            // 更新最小列范围  
            minCol = Math.min(minCol, cols);
        }
        
        // 最大值区域是左上角的 minRow × minCol 矩形
        // 但不能超过矩阵本身的大小
        int resultRow = Math.min(minRow, m);
        int resultCol = Math.min(minCol, n);
        
        return resultRow * resultCol;
    }
}

优化(更简洁)

class Solution {
    /**
     * 优化:代码更简洁
     * 
     * @param m 矩阵行数
     * @param n 矩阵列数
     * @param ops 操作数组
     * @return 最大整数的元素个数
     */
    public int maxCount(int m, int n, int[][] ops) {
        int minRow = m;  // 初始值设为矩阵行数
        int minCol = n;  // 初始值设为矩阵列数
        
        // 遍历所有操作,找最小的行和列范围
        for (int[] op : ops) {
            minRow = Math.min(minRow, op[0]);
            minCol = Math.min(minCol, op[1]);
        }
        
        return minRow * minCol;
    }
}

算法分析

  • 时间复杂度:O(k)

    • k 是操作次数
    • 需要遍历所有操作一次
  • 空间复杂度:O(1)

    • 只使用常数额外空间
  • 关键

    • 避免了实际构建和操作矩阵
    • 直接通过数学分析得出结果
    • 时间复杂度从 O(k×m×n) 优化到 O(k)

算法过程

m=3, n=3, ops=[[2,2],[3,3]]

1:分析每个操作的影响区域

  • 操作 [2,2]:影响 [0,2) × [0,2) = 前2行前2列
  • 操作 [3,3]:影响 [0,3) × [0,3) = 全部3×3区域

2:找所有操作的交集

  • 行范围交集:min(2,3) = 2
  • 列范围交集:min(2,3) = 2
  • 公共区域:2×2 的左上角区域

3:计算结果

  • 该区域被所有操作覆盖,值为2(操作次数)
  • 区域大小:2 × 2 = 4
  • 返回 4

验证过程

初始: [[0,0,0],
       [0,0,0], 
       [0,0,0]]

操作[2,2]: [[1,1,0],
           [1,1,0],
           [0,0,0]]

操作[3,3]: [[2,2,1],
           [2,2,1],
           [1,1,1]]

最大值2出现了4次,正确。

测试用例

public static void main(String[] args) {
    Solution solution = new Solution();
    
    // 测试用例1:标准示例
    int[][] ops1 = {{2,2},{3,3}};
    System.out.println("Test 1: " + solution.maxCount(3, 3, ops1)); // 4
    
    // 测试用例2:无操作
    int[][] ops2 = {};
    System.out.println("Test 2: " + solution.maxCount(3, 3, ops2)); // 9
    // 所有元素都是0,最大值0出现9次
    
    // 测试用例3:操作范围超出矩阵
    int[][] ops3 = {{4,4},{2,2}};
    System.out.println("Test 3: " + solution.maxCount(3, 3, ops3)); // 4
    // minRow=min(4,2)=2, minCol=min(4,2)=2 → 2×2=4
    
    // 测试用例4:单个操作
    int[][] ops4 = {{1,1}};
    System.out.println("Test 4: " + solution.maxCount(3, 3, ops4)); // 1
    // 只有[0,0]位置被加1
    
    // 测试用例5:多个操作,最小范围为1
    int[][] ops5 = {{1,3},{3,1},{2,2}};
    System.out.println("Test 5: " + solution.maxCount(3, 3, ops5)); // 1
    // minRow=min(1,3,2)=1, minCol=min(3,1,2)=1 → 1×1=1
    
    // 测试用例6:大矩阵小操作
    int[][] ops6 = {{2,2},{2,2}};
    System.out.println("Test 6: " + solution.maxCount(100, 100, ops6)); // 4
    // 与矩阵大小无关,只与操作有关
}

关键点

  1. 交集思想

    • 被最多次操作覆盖的区域 = 所有操作区域的交集
    • 交集的大小由最小的操作范围决定
  2. 避免暴力模拟

    • 如果实际模拟每次操作,时间复杂度为 O(k×m×n)
    • 通过数学分析优化到 O(k)
  3. 边界处理

    • 无操作时,所有元素都是0,最大值出现 m×n 次
    • 操作范围可能大于矩阵大小,需取最小值
  4. 贪心正确性证明

    • 任何不在 [0,minRow)×[0,minCol) 的位置
    • 至少被一个操作遗漏(因为该操作的范围更小)
    • 所以这些位置的值 < k
    • 只有交集区域的值 = k

常见问题

  1. 为什么只需要找最小值?

    • 因为操作区域都是从 (0,0) 开始的矩形
    • 所有操作都覆盖的区域由最小的行和列范围决定
    • 这是矩形交集的性质
  2. 如果操作不是从(0,0)开始怎么办?

    • 本题的操作都是从左上角开始
    • 如果是任意矩形,就需要更复杂的区间树或差分数组
  3. 矩阵大小m,n的作用?

    • 在优化版本中似乎没用到
    • 实际上,当 minRow > mminCol > n 时需要限制
    • 但在代码中,直接用 minRow = min(minRow, op[0]),初始值为m,所以自动处理了边界
  4. 算法的直观理解

    • 想象每次操作都在左上角贴一个矩形贴纸
    • 被所有贴纸都覆盖的区域就是重叠最多的区域
    • 这个区域的大小由最小的贴纸决定
  5. 与区间更新的区别

    • 本题是多次区间加法,但起始点固定
    • 如果是任意区间的更新,通常用差分数组或线段树
    • 本题的特殊性允许O(1)空间解法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值