2397. 被列覆盖的最多行数(二进制枚举+Gosper‘s Hack)

题目链接:2397. 被列覆盖的最多行数

二进制枚举

此题整体思路是通过枚举选取了哪些列,然后与每行进行比对,进而计算出选取对应列所能覆盖的行数。那么可以把本问题提炼出两个优化点:

  1. 如何快速准确枚举对应列。
  2. 如何快速比较选取出的列能否覆盖对应的行。

上述两个问题都可以利用二进制来解决。

枚举列

对于 如何快速准确枚举对应列:
由于集合可以由全集(包含所有元素的集合)中每个元素的选或者不选来表示,因此,很容易联想到二进制上每一位的 0 和 1,例如 101=5表示集合中只有第 0 个元素和第 2 个元素,即{0,2}。
要枚举所有列的选取情况,假设有n列,那么相当于集合从空集遍历到全集,即{col1,col2,…,coln},对应的二进制从0遍历到2n-1。在循环内部判断二进制中1的个数是否与题目要求相符。

for (int s = 0; s < (1 << n); s++) {
	if(Integer.bitCount(s) == cols){
		continue;
	} 
	// 处理 s 的逻辑
}

对于 如何快速比较选取出的列能否覆盖对应的行:

同样把每一行也看成二进制数,转换代码如下:

for (int j = n-1; j >= 0; j--){
    cur += (matrix[i][j] << (j));
}

假设当前行为cur,当前选取的列为s。如果二者相与还是等于当前行cur,说明cur每一列的1在选取的列中都有对应,故可取。

if(cur & s == cur){
	count++;
}

完整代码如下:

class Solution {
    public int maximumRows(int[][] matrix, int numSelect) {
        int n = matrix[0].length;//列数
        int max = 0;
        for (int s = 0; s < (1 << n); s++) {
            if(Integer.bitCount(s) != numSelect){
                continue;
            }
            int count = 0;
            for(int i = 0; i < matrix.length; i++){
                int cur = 0;
                for (int j = n-1; j >= 0; j--){
                    cur += (matrix[i][j] << (j));
                }
                if((cur & s) == cur){
                    count++;
                }
            }
            if(count > max){
                max = count;
            }
        }
        return max;
    }
}

在这里插入图片描述

Gosper’s Hack

上面的代码有很多无效枚举,即大小不等于 cols 的集合,每次也要枚举进来。试想是否有一种方法,让每次枚举的二进制数 1 的个数恰好等于 cols 。

Gosper’s Hack 算法是生成 n 元集合中所有包含 k 个元素的子集的算法。

这里先给出 Gosper’s Hack 算法的代码:

while (x < uplimit) {
    int lowbit = x & (-x);
    int left = x + lowbit;
    int right = ((x ^ (x + lowbit)) / lowbit) >> 2;
    x = left | right;
}

接下来讲一下 Gopser’s Hack 算法的思想:

对一个二进制数,例如 110110,我们需要找到它从左往右的最后一个 01,然后把这个 01 变成 10,再把它右边的 1 全部集中到最右边(这里右边的 1 显然都是连续的,否则与最后一个 01 矛盾),即 110110→111001。

在这里插入图片描述
Gopser’s Hack 都是数学原理,现场想肯定想不来, 之后用二进制枚举就够了。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值