每日一道leetcode(2026.03.26):等和矩阵分割 II

每日一道leetcode(2026.03.26):等和矩阵分割 II

  • [1. 题目](#1. 题目)
  • [2. 分析](#2. 分析)
  • [3. 代码实现](#3. 代码实现)
  • [4. 总结](#4. 总结)

1. 题目

给你一个由正整数组成的 m x n 矩阵 grid。你的任务是判断是否可以通过 一条水平或一条垂直分割线 将矩阵分割成两部分,使得:

  • 分割后形成的每个部分都是 非空 的。
  • 两个部分中所有元素的和 相等 ,或者总共 最多移除一个单元格 (从其中一个部分中)的情况下可以使它们相等。
  • 如果移除某个单元格,剩余部分必须保持 连通 。
    如果存在这样的分割,返回 true;否则,返回 false。

注意: 如果一个部分中的每个单元格都可以通过向上、向下、向左或向右移动到达同一部分中的其他单元格,则认为这一部分是 连通 的。

示例 1:

输入: grid = [[1,4],[2,3]]

输出: true

解释:

在第 0 行和第 1 行之间进行水平分割,结果两部分的元素和为 1 + 4 = 5 和 2 + 3 = 5,相等。因此答案是 true。

2. 分析

这天这道题是昨天等和矩阵分割 I的升级版,题目依然很好理解,一开始我觉着应该挺简单,但是看标签,觉着肯定有坑在等着我,已经做好了提交后超时的心理准备。

还是沿着昨天的思路,先计算出所有的元素之和,然后移动下标,计算每行每列的和之后,对两部分的和值进行对应的加减,下面为了便于叙述,我只描述纵向移动的过程,横向移动以此类推。

如果是移动到了某个位置,两边的和值相等,那么皆大欢喜,直接返回true即可,如果是上边大于下边,则需要判断上边的所有元素中是否有一个元素等于上下的差值。这里就得分情况判断了,如果上面只有一行,那么只能移除两端的那两个元素,否则会影响连通性,如果有多行,则可以移除上面的任意一个元素。还有更极端的,如果原始输入是nx1或者1xn,这里分割后上面就是一列,那么只能移除上下两端的元素了。如果是一端只有一个元素了,那么是否需要做补充判断呢,应该是不需要的,移除后一端和值就变成0了,永远不会相等了。

按照这个大体的思路,有了如下实现。

3. 代码实现

java 复制代码
class Solution {
    public boolean canPartitionGrid(int[][] grid) {
        // 计算所有的和
        long sum = 0;
        // 记录每行的元素和位置
        List<List<Integer>> rows = new ArrayList<>();
        // 每列
        List<List<Integer>> cols = new ArrayList<>();
        // 每行之和
        long[] rowSums = new long[grid.length];
        // 每列之和
        long[] colSums = new long[grid[0].length];

        Set<Integer> set = new HashSet<>();
        // 每行之和
        List<Set<Integer>> rowSet = new ArrayList<>();
        // 每列之和
        List<Set<Integer>> colSet = new ArrayList<>();
        // 最大值
        int max = 0;
        // 最小值
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < grid.length; i++) {
            rows.add(new ArrayList<>());
            rowSet.add(new HashSet<>());
            for (int j = 0; j < grid[i].length; j++) {
                if (cols.size() < j + 1) {
                    cols.add(new ArrayList<>());
                    colSet.add(new HashSet<>());
                }
                int val = grid[i][j];
                sum += val;
                rows.get(i).add(val);
                rowSums[i] += val;
                cols.get(j).add(val);
                colSums[j] += val;
                set.add(val);
                rowSet.get(i).add(val);
                colSet.get(j).add(val);
                max = Math.max(max, val);
                min = Math.min(min, val);
            }
        }
        return moveAndCheck(rows, rowSums, set, rowSet, sum, max, min) || moveAndCheck(cols, colSums, set, colSet, sum, max, min);
    }

    public boolean moveAndCheck(List<List<Integer>> eleList, long[] sumArray, Set<Integer> set, List<Set<Integer>> numSet, long sum, int max, int min) {
        long up = 0;
        long down = sum;
        for (int i = 0; i < eleList.size(); i++) {
            // 计算第i行的和
            up += sumArray[i];
            down -= sumArray[i];
            if (up == down) {
                return true;
            } else if (up > down) {
                // 判断上方是否有一个元素等于差值
                long diff = up - down;
                if (diff < min || diff > max || !set.contains((int) diff)) {
                    continue;
                }
                if (existDiff(eleList, 0, i, numSet, diff)) {
                    return true;
                }
            } else {
                // 判断下方是否有一个元素等于差值
                long diff = down - up;
                if (diff < min || diff > max || !set.contains((int) diff)) {
                    continue;
                }
                if (existDiff(eleList, i + 1, eleList.size() - 1, numSet, diff)) {
                    return true;
                }
                // 继续移动
            }
        }
        return false;
    }

    public boolean existDiff(List<List<Integer>> eleList, int from, int to, List<Set<Integer>> numSet, long diff) {
        // 左边或者右边只剩下一行或一列时,只能移除掉端上的一个元素,否则会影响连通性
        if (from == to) {
            List<Integer> ele = eleList.get(from);
            return ele.get(0) == diff || ele.get(ele.size() - 1) == diff;
        } else if (eleList.get(0).size() == 1) {
            // 原始输入只有一行时
            return eleList.get(from).get(0) == diff || eleList.get(to).get(0) == diff;
        } else if (eleList.size() == 1) {
            // 原始输入只有一列时
            return eleList.get(0).get(from) == diff || eleList.get(0).get(to) == diff;
        }
        // 存在多行多列时,所有的元素都可以移除,不影响连通性
        for (int i = from; i <= to; i++) {
            if (numSet.get(i).contains((int) diff)) {
                return true;
            }
        }
        return false;
    }
}

4. 总结

勉强通过了,过程中肯定遇到了计算超时的问题,做了部分优化。官方的题解使用到了矩阵旋转,这个我暂时没想到。过程中,有想过正向和方向移动来减少对排除元素的遍历,可能会对性能上还有部分优化,有兴趣的朋友可以试试。还是以上下移动为例,当出现不相等时,仅判断上面大于下面的情况,把上面的所有元素假如到集合中,可以快速定位到是否存在等于差值的元素,再补充一个从下往上移动的遍历,仅需要判断下面大于上面的情况,下方是否存在等于差值的元素。

相关推荐
啊我不会诶2 小时前
2025ICPC南昌邀请赛vp补题
算法
发发就是发2 小时前
I2C适配器与算法:从一次诡异的时序问题说起
服务器·驱动开发·单片机·嵌入式硬件·算法·fpga开发
啊哦呃咦唔鱼2 小时前
leetcode二分查找
数据结构·算法·leetcode
郝学胜-神的一滴2 小时前
[ 力扣 1124 ] 解锁最长良好时段问题:前缀和+哈希表的优雅解法
java·开发语言·数据结构·python·算法·leetcode·散列表
戴西软件2 小时前
戴西CAxWorks.VPG车辆工程仿真软件|假人+座椅双调整 汽车仿真效率直接拉满
java·开发语言·人工智能·python·算法·ui·汽车
Tairitsu_H2 小时前
C++入门指南:从基础语法到核心特性全解析
c++·算法·基础
programhelp_2 小时前
2026 高盛(Goldman Sachs)Coding Interview 真题分享|Design HashMap + 其他面试题完整解析
算法·哈希算法
Pentane.2 小时前
力扣HOT100:T.1 两数之和|循环遍历算法笔记及打卡(12/100)
c++·笔记·算法·leetcode
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【线性扫描贪心】:士兵站队
c++·算法·贪心算法·csp·信奥赛·线性扫描贪心·士兵战队
无限进步_2 小时前
二叉树的中序遍历(非递归实现)
开发语言·数据结构·c++·windows·算法·visual studio