LeetCode 每日一题
2397. 被列覆盖的最多行数
问题
给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix ;另给你一个整数 numSelect,表示你必须从 matrix 中选择的 不同 列的数量。
如果一行中所有的 1 都被你选中的列所覆盖,则认为这一行被 覆盖 了。
形式上,假设 s = {c1, c2, ...., cnumSelect} 是你选择的列的集合。对于矩阵中的某一行 row ,如果满足下述条件,则认为这一行被集合 s 覆盖:
对于满足 matrix[row][col] == 1 的每个单元格 matrix[row][col](0 <= col <= n - 1),col 均存在于 s 中,或者
row 中 不存在 值为 1 的单元格。
你需要从矩阵中选出 numSelect 个列,使集合覆盖的行数最大化。
返回一个整数,表示可以由 numSelect 列构成的集合 覆盖 的 最大行数 。
解答
- 看到矩阵首先想到 状态压缩
- 任何题目都先思考 枚举
- 看到 枚举为何/二选一 状态时,可以考虑 动态规划,然后才更进一步地考虑贪心
- 看到 覆盖问题,可以考虑 DLX 的思想,覆盖问题其实也是搜索问题,而搜索算法,本质上,就是枚举 + 递归
- 依据自动机理论,优先查看终止状态的特征
首先思考能否通过枚举实现,显然这是可行的,毕竟它的范围比较小,于是可以直接枚举每种选择,毕竟选择的最多次数也不过是
而在枚举中存在技巧,因为给定了 选取 列 的最大个数 numSelect,于是我们可以利用 Gosper's Hack算法进行优化,简而言之,该算法就是求出 长度为 n 的比特序列中,有 k 个元素为 1 、其他元素为 0 的所有序列
它的思想是找到第一个 01 将其改成 10,并其后的连续的 1 移动到序列末尾,显然在这个过程中 得到的序列比原有的打,切在其中间不再存在满足上述条件(有 k 个元素为 1 、其他元素为 0 的所有序列)的序列
设当前序列为 x,其具体实现为
- 找到第一个 01 将其改成 10:
l = x + x & -x
,其中x & -x
表示 x 中从第一个 1 截断后得到的值,被称为 lowbit,此时 第一个 1 后面全为 0 - 其后的连续的 1 移动到序列末尾:
r = (x ^ l) >> (lowbit 中 0 的个数 + 2)
,其中,(x ^ l) 将 x第一个 01 将其改成 11,其左边的变成 0,右边的不变,lowbit 中 0 的个数也即是 (x ^ l)的 0 的个数,2 是因为 01 变成了 11 ,其 01 的 1 已经在 l 中了,所以要再右移 2 位 - 结果 = l | r
代码:
class Solution:
def maximumRows(self, matrix: List[List[int]], numSelect: int) -> int:
m, n = len(matrix), len(matrix[0])
mask = [sum(v << j for j, v in enumerate(row)) for i, row in enumerate(matrix)]
res, limit = 0, 1 << n
for cur in range(1, limit):
if cur.bit_count() != numSelect:
continue
t = sum((mask[j] & cur) == mask[j] for j in range(m))
res = max(res, t)
return res
# 作者:力扣官方题解
# 链接:https://leetcode.cn/problems/maximum-rows-covered-by-columns/solutions/2587986/bei-lie-fu-gai-de-zui-duo-xing-shu-by-le-5kb9/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
def count_trailing_zeros(x):
return (x & -x).bit_length() - 1
class Solution:
def maximumRows(self, matrix: List[List[int]], numSelect: int) -> int:
m, n = len(matrix), len(matrix[0])
mask = [sum(v << j for j, v in enumerate(row)) for i, row in enumerate(matrix)]
res, cur = 0, (1 << numSelect) - 1
limit = 1 << n
while cur < limit:
t = sum((mask[j] & cur) == mask[j] for j in range(m))
res = max(res, t)
lb = cur & -cur
r = cur + lb
cur = ((r ^ cur) >> count_trailing_zeros(lb) + 2) | r
return res
# 作者:力扣官方题解
# 链接:https://leetcode.cn/problems/maximum-rows-covered-by-columns/solutions/2587986/bei-lie-fu-gai-de-zui-duo-xing-shu-by-le-5kb9/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
拓展
依据灵神的网站题目顺序
1823. 找出游戏的获胜者
问题
共有 n 名小伙伴一起做游戏。小伙伴们围成一圈,按 顺时针顺序 从 1 到 n 编号。确切地说,从第 i 名小伙伴顺时针移动一位会到达第 (i+1) 名小伙伴的位置,其中 1 <= i < n ,从第 n 名小伙伴顺时针移动一位会回到第 1 名小伙伴的位置。
游戏遵循如下规则:
从第 1 名小伙伴所在位置 开始 。
沿着顺时针方向数 k 名小伙伴,计数时需要 包含 起始时的那位小伙伴。逐个绕圈进行计数,一些小伙伴可能会被数过不止一次。
你数到的最后一名小伙伴需要离开圈子,并视作输掉游戏。
如果圈子中仍然有不止一名小伙伴,从刚刚输掉的小伙伴的 顺时针下一位 小伙伴 开始,回到步骤 2 继续执行。
否则,圈子中最后一名小伙伴赢得游戏。
给你参与游戏的小伙伴总数 n ,和一个整数 k ,返回游戏的获胜者。
解答
很显然地存在子问题,构造子问题集,查看其之间的关系,然后dp
代码:
class Solution:
def findTheWinner(self, n: int, k: int) -> int:
x = 0
for i in range(2, n + 1):
x = (x + k) % i
return x + 1
# 作者:Krahets
# 链接:https://leetcode.cn/problems/find-the-winner-of-the-circular-game/solutions/2362052/1823-zhao-chu-you-xi-de-huo-sheng-zhe-yu-tkst/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
拓展
问题
解答
代码:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了