LeetCode 2732. 找到矩阵中的好子集

一、题目描述

给定一个 m x n 的整数矩阵 mat 和一个整数 k,我们需要找到一个大小为 k 的子集 rows,使得这个子集对应的行在矩阵 mat 中构成的子矩阵中,所有元素之和最大。返回这个子矩阵中所有元素之和的最大值。

注意

  • 子集 rows 中的每个元素(即行索引)都应该是唯一的。
  • 子集 rows 的大小应为 k

示例

输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 2

输出:18

解释:选择第 1 行和第 2 行,得到子矩阵 [[4,5,6],[7,8,9]],子矩阵中所有元素之和为 18。

二、解题思路

为了解决这个问题,我们需要从几个不同的角度进行思考。首先,由于题目要求返回的是子矩阵中所有元素之和的最大值,我们可以考虑使用贪心算法来尝试解决这个问题。但是,贪心算法在这里可能并不适用,因为我们不能保证每一步的选择都是最优的。

其次,我们可以考虑使用回溯法(Backtracking)来解决这个问题。回溯法是一种通过穷举所有可能的解来找出所有解的算法。在这个问题中,我们可以将回溯法应用于选择行索引的过程中,通过递归地尝试所有可能的组合,来找到满足条件的最优解。

具体的解题步骤如下:

  1. 定义回溯函数:我们需要定义一个回溯函数,该函数接受当前选择的行索引集合、当前选择的行索引数量以及当前子矩阵的元素和作为参数。
  2. 判断终止条件 :在回溯函数中,我们首先判断当前选择的行索引数量是否达到了 k。如果达到了 k,则更新最大元素和(如果当前子矩阵的元素和大于之前的最大元素和)。
  3. 递归选择:然后,我们遍历矩阵中的每一行,对于每一行,我们有两种选择:选择该行(将其添加到当前选择的行索引集合中,并递归调用回溯函数处理下一行),或者不选择该行(直接递归调用回溯函数处理下一行)。
  4. 回溯操作:在递归调用完成后,我们需要将当前选择的行索引从集合中移除,以便尝试其他可能的组合。
  5. 初始化参数并调用回溯函数 :最后,我们初始化最大元素和为一个较小的值(如0),并调用回溯函数开始处理第一行。
    但除了回溯法之外,如果矩阵的行数不是非常大,我们还可以考虑使用动态规划(Dynamic Programming)结合位运算来优化解决方案。以下是两种代码实现:

1. 回溯法(Backtracking)

python 复制代码
def goodSubsetSum(mat, k):
    m, n = len(mat), len(mat[0])
    max_sum = 0

    def backtrack(rows, curr_sum, start):
        nonlocal max_sum
        if len(rows) == k:
            max_sum = max(max_sum, curr_sum)
            return

        for i in range(start, m):
            if i > start and mat[i] == mat[i - 1]:  # 避免重复选择相同的行
                continue
            backtrack(rows + [i], curr_sum + sum(mat[i]), i + 1)

    backtrack([], 0, 0)
    return max_sum

# 示例测试
mat = [[1,2,3],[4,5,6],[7,8,9]]
k = 2
print(goodSubsetSum(mat, k))  # 输出: 18

接下来,我们可以考虑使用动态规划的方法。然而,动态规划通常需要定义一个状态数组来保存中间结果,但在这个问题中,由于我们需要选择的是行索引,而不是具体的元素值,因此状态的定义可能会变得比较复杂。

2. 动态规划结合位运算(Dynamic Programming with Bit Manipulation)

由于矩阵中的行是唯一的(或者可以视为唯一,如避免选择重复行),我们可以使用位掩码(bitmask)来表示一个行集合。对于m行矩阵,我们可以使用一个m位的二进制数来表示选择了哪些行。

python 复制代码
def goodSubsetSum(mat, k):
    m, n = len(mat), len(mat[0])
    dp = [0] * (1 << m)  # 初始化dp数组,大小为2^m

    # 初始化只选择一行的情况
    for i in range(m):
        dp[1 << i] = sum(mat[i])

    # 动态规划填充dp数组
    for mask in range(1, 1 << m):
        if bin(mask).count('1') <= k:  # 确保选择的行数不超过k
            for i in range(m):
                if mask & (1 << i):  # 如果第i行被选择
                    # 尝试移除第i行并更新最大和(实际上不会真的移除,只是计算不包括第i行的和)
                    next_mask = mask ^ (1 << i)
                    dp[mask] = max(dp[mask], dp[next_mask] + sum(mat[i]))

    # 查找包含k行的最大和
    max_sum = 0
    for mask in range(1 << m):
        if bin(mask).count('1') == k:
            max_sum = max(max_sum, dp[mask])

    return max_sum

# 示例测试
mat = [[1,2,3],[4,5,6],[7,8,9]]
k = 2
print(goodSubsetSum(mat, k))  # 输出: 18

注意:动态规划结合位运算的方法在m(行数)较大时可能会因为空间复杂度过高而不太实用,因为它需要一个大小为2^m的数组来存储中间结果。然而,在m较小的情况下,这种方法可能会比回溯法更快,因为它避免了不必要的重复计算。

相关推荐
2601_957884849 分钟前
AI赋能的内容工程学:短视频矩阵系统的多模态内容生成与量产边界
人工智能·矩阵·音视频
黎阳之光10 分钟前
视频孪生+空天地水工融合,黎阳之光构建智慧水利监测新范式
大数据·人工智能·物联网·算法·安全
cheems952722 分钟前
[算法手记] 贪心 爬楼梯问题
算法·贪心算法
KaMeidebaby34 分钟前
卡梅德生物技术快报|酵母双杂交 cDNA 文库构建与蛋白互作筛选流程
服务器·前端·数据库·人工智能·算法
圣保罗的大教堂41 分钟前
leetcode 3300. 替换为数位和以后的最小元素 简单
leetcode
sheeta199842 分钟前
LeetCode 每日一题笔记 日期:2026.05.27 题目:3121. 统计特殊字母的数量 II
笔记·算法·leetcode
ST——Jess1 小时前
年度行业趋势研究报告:泛心理数字化赛道“流日推演”的算法困境与高保真交互范式重构
人工智能·算法·架构
Tisfy1 小时前
LeetCode 3300.替换为数位和以后的最小元素:一次遍历
数学·算法·leetcode·模拟
garmin Chen1 小时前
LeetcodeHot100打卡(14、合并空间,15、轮转数组,16、除了自身以外数组乘积,17.缺失的第一个整数)
java·笔记·学习·算法