leetcode
leetcode 2351 ~ 2400
找到矩阵中的好子集

找到矩阵中的好子集

难度:

标签:

题目描述

You are given a 0-indexed m x n binary matrix grid.

Let us call a non-empty subset of rows good if the sum of each column of the subset is at most half of the length of the subset.

More formally, if the length of the chosen subset of rows is k, then the sum of each column should be at most floor(k / 2).

Return an integer array that contains row indices of a good subset sorted in ascending order.

If there are multiple good subsets, you can return any of them. If there are no good subsets, return an empty array.

A subset of rows of the matrix grid is any matrix that can be obtained by deleting some (possibly none or all) rows from grid.

 

Example 1:

Input: grid = [[0,1,1,0],[0,0,0,1],[1,1,1,1]]
Output: [0,1]
Explanation: We can choose the 0th and 1st rows to create a good subset of rows.
The length of the chosen subset is 2.
- The sum of the 0th column is 0 + 0 = 0, which is at most half of the length of the subset.
- The sum of the 1st column is 1 + 0 = 1, which is at most half of the length of the subset.
- The sum of the 2nd column is 1 + 0 = 1, which is at most half of the length of the subset.
- The sum of the 3rd column is 0 + 1 = 1, which is at most half of the length of the subset.

Example 2:

Input: grid = [[0]]
Output: [0]
Explanation: We can choose the 0th row to create a good subset of rows.
The length of the chosen subset is 1.
- The sum of the 0th column is 0, which is at most half of the length of the subset.

Example 3:

Input: grid = [[1,1,1],[1,1,1]]
Output: []
Explanation: It is impossible to choose any subset of rows to create a good subset.

 

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m <= 104
  • 1 <= n <= 5
  • grid[i][j] is either 0 or 1.

代码结果

运行时间: 98 ms, 内存: 20.0 MB


// 思路:
// 1. 使用Java Stream来简化遍历和计算。
// 2. 通过Stream生成所有可能的行子集,并检查是否为好子集。
// 3. 返回满足条件的子集的行下标。

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Solution {
    public List<Integer> goodSubset(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;
        return IntStream.range(1, 1 << m)
                .mapToObj(i -> {
                    List<Integer> subset = new ArrayList<>();
                    int[] colSums = new int[n];
                    int k = 0;
                    for (int j = 0; j < m; j++) {
                        if ((i & (1 << j)) != 0) {
                            subset.add(j);
                            k++;
                            for (int l = 0; l < n; l++) {
                                colSums[l] += grid[j][l];
                            }
                        }
                    }
                    boolean isGoodSubset = IntStream.of(colSums).allMatch(sum -> sum <= k / 2);
                    return isGoodSubset ? subset : null;
                })
                .filter(subset -> subset != null)
                .findFirst()
                .orElse(new ArrayList<>());
    }
}

解释

方法:

题解的核心思想是利用二进制表示每行的内容,并通过两两查找是否存在互不相交的行,即这两行在任何一列都没有同时出现1。首先,将每行的二进制数存入字典seen中,键是行的二进制数,值是行的索引。然后,对于字典中的每个键值对,检查是否存在两个不同的键(即两行),它们的二进制与操作结果为0,这代表这两行在任何一列都不会同时出现1。如果找到这样的两行,它们就构成一个好子集。如果所有行都被检查过后没有找到这样的两行,那么返回一个空数组。

时间复杂度:

O(n*m + 4^m)

空间复杂度:

O(2^m)

代码细节讲解

🦆
在你的算法中,你是如何保证遍历的二进制数能够有效地代表矩阵中的每一行?
在算法中,通过遍历矩阵的每一行,然后使用一个循环将每行的元素(0或1)转换为一个二进制数,这个过程是精确的,可以保证每一行都被有效地转换成一个唯一的二进制数。这个二进制数是通过从左到右读取行中的元素并将其视为二进制位来构建的,其中`1`代表相应位置有元素,`0`代表没有。因此,通过这种方式可以确保每行数据的唯一性和正确的二进制表示。
🦆
算法中提到的`seen`字典将每行的二进制数映射到行索引,但如果两行相同怎样处理?是否只保存一个索引会影响结果?
在题解中,`seen`字典用来存储行的二进制表示及其索引。如果矩阵中有两行完全相同,则它们的二进制表示也会相同。在这种情况下,字典会覆盖之前存储的索引,因此只有最后一次出现的行索引会被保存。这确实可能导致某些好子集的遗漏,因为原始的多个好子集可能因为索引覆盖而无法完全识别出来。因此,为了更全面地维护所有行的信息,应该将`seen`字典中的值修改为索引列表而不是单个索引。
🦆
在实现中,对所有可能的二进制数进行了遍历检查,这种方法的时间复杂度是多少?是否存在更高效的方式来找到好子集?
当前实现中,算法遍历了所有可能的二进制数(从0到2^m-1),其中m是矩阵的列数。对于每一个二进制数a,算法又遍历所有可能的二进制数b进行比较。这导致时间复杂度为O((2^m) * (2^m)),即O(4^m),这在m较大时非常低效。存在更高效的方法,例如使用图论中的匹配算法来找出互不相交的行集合,或者利用动态规划和位运算结合的方法来优化查找过程。这些方法可以在保证找到解的同时显著减少计算量。

相关问题