leetcode-位运算/状态压缩

理解位运算

关于位运算看这个就够了 - 掘金

一文搞明白位运算、补码、反码、原码 - 掘金

左移一位相当于乘以2,右移一位相当于除以2

六十、走进位运算的大门 - 掘金

原理

 

为什么做运算的是补码

Python基础之位运算符(含原码反码补码的通俗解释)_化简可得-CSDN博客

78 子集

for i in range(1 << n):

表示枚举所有状态

if (i >> j) & 1:
        tmp.append(nums[j])

表示找到当前状态下,为1的位置,并从nums中找到对应的数字。

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        res = []
        for i in range(1 << n):
            tmp = []
            for j in range(n):
                if (i >> j) & 1:
                    tmp.append(nums[j])
            res.append(tmp)
        return res

作者:edelweisskoko
链接:https://leetcode-cn.com/problems/subsets/solution/78-zi-ji-hui-su-jie-fa-wei-yun-suan-jie-xlryd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

5978. 统计追加字母可以获得的单词数

状态压缩

 

class Solution:
    def wordCount(self, startWords: List[str], targetWords: List[str]) -> int:
        s = set()
        for word in startWords:
            mask = 0
            for ch in word:
                mask |= 1 << (ord(ch) - ord('a'))
            s.add(mask)
        ans = 0
        for word in targetWords:
            mask = 0
            for ch in word:
                mask |= 1 << (ord(ch) - ord('a'))
            for ch in word:
                if mask ^ (1 << (ord(ch) - ord('a'))) in s:  # 去掉这个字符
                    ans += 1
                    break
        return ans

作者:endlesscheng
链接:https://leetcode-cn.com/problems/count-words-obtained-after-adding-a-letter/solution/ni-xiang-si-wei-wei-yun-suan-ha-xi-biao-l4153/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

对if mask ^ (1 << (ord(ch) - ord('a'))) in s:  # 去掉这个字符的理解:

按位异或,如果mask是1 ,那么按位异或之后就是0,即可去掉当前这个字符

class Solution(object):
    def wordCount(self, startWords, targetWords):
        """
        :type startWords: List[str]
        :type targetWords: List[str]
        :rtype: int
        """
        #把startWords里的单词转化为二进制。
        #把targetwords里的单词转化为二进制
        #根据异或性质,两位相同为0,不同为1,逐个字母判断去掉之后是否在startWords里

        #先把startWords转化为二进制
        ans=0
        s=set()
        for word in startWords:
            mask=0
            for ch in word:
                mask|=1<<(ord(ch)-ord("a"))
            s.add(mask)
        
        #把targetwords里的单词转化为二进制
        for word in targetWords:
            mask=0
            for ch in word:
                mask|=1<<(ord(ch)-ord("a"))
            #判断去掉一个之后,有没有在startword里
            for ch in word:
                if mask^(1<<(ord(ch)-ord("a"))) in s:
                    ans+=1
                    break 
        return ans
            

2157. 字符串分组

状态压缩

 

对于下面这段代码的理解

        # 将一个 0 变成 1,或将一个 1 变成 0
            # 按位异或,两位不同时为1,相同为0
            for i in range(26):
                adj.append(mask ^ (1 << i))

按位异或的性质:

 1<<i:表示把1左移动i位置,假如当i=2时,1<<2是 100,第三位是1。

假如mask的第三位是1,那么异或之后,第三位0;假如mask的第三位是0,那么异或之后,第三位1 。其余位置和0异或,结果不变。

对下面代码的理解

# 将一个 0 变成 1,且将一个 1 变成 0
            for i in range(26):
                if mask & (1 << i):
                    for j in range(26):
                        if not (mask & (1 << j)):
                            adj.append(mask ^ (1 << i) ^ (1 << j))

按位与的性质:

mask & (1 << i) 第i位是1
if not (mask & (1 << j)) 第j位不是1(是0)
class Solution:
    def groupStrings(self, words: List[str]) -> List[int]:
        # 使用哈希映射统计每一个二进制表示出现的次数
        wordmasks = Counter()
        for word in words:
            mask = 0
            for ch in word:
                mask |= (1 << (ord(ch) - ord("a")))
            wordmasks[mask] += 1
        
        # 辅助函数,用来得到 mask 的所有可能的相邻节点
        def get_adjacent(mask: int) -> List[int]:
            adj = list()
            # 将一个 0 变成 1,或将一个 1 变成 0
            for i in range(26):
                adj.append(mask ^ (1 << i))
            # 将一个 0 变成 1,且将一个 1 变成 0
            for i in range(26):
                if mask & (1 << i):
                    for j in range(26):
                        if not (mask & (1 << j)):
                            adj.append(mask ^ (1 << i) ^ (1 << j))
            return adj
        
        used = set()
        best = cnt = 0
        for mask, occ in wordmasks.items():
            if mask in used:
                continue
            
            # 从一个未搜索过的节点开始进行广度优先搜索,并求出对应连通分量的大小
            q = deque([mask])
            used.add(mask)
            # total 记录联通分量的大小
            total = occ

            while q:
                u = q.popleft()
                for v in get_adjacent(u):
                    if v in wordmasks and v not in used:
                        q.append(v)
                        used.add(v)
                        total += wordmasks[v]
            
            best = max(best, total)
            cnt += 1
            
        return [cnt, best]

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/groups-of-strings/solution/zi-fu-chuan-fen-zu-by-leetcode-solution-a8dr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6007. 数组的最大与和

状态压缩+记忆化深搜

 

 1.为啥要把长度*2

每个篮子可以装2个,长度不*2就只能判断装没装,不知道装几个了

2.state 就是 2 ** (2 * l) - 1,这时候就装满了,全部状态位都是1

class Solution:
    def maximumANDSum(self, nums: List[int], numSlots: int) -> int:
        l = numSlots
        ln = len(nums)
        @lru_cache(None)
        def dfs(i, state):
            if i >= ln:
                return 0
            n = nums[i]
            res = 0
            for j in range(2 * l):
              #遍历每个二进制位,是0的进行填充。
              #可以包括每种情况,因为最开始就是从0到2*l进行遍历的
                if state & (1 << j) == 0:
                    res = max(res, (n & (j // 2 + 1)) + dfs(i + 1, state | (1 << j)))
            return res
        return dfs(0, 0)
            

作者:qin-qi-shu-hua-2
链接:https://leetcode-cn.com/problems/maximum-and-sum-of-array/solution/python-zhuang-ya-dfs-by-qin-qi-shu-hua-2-hnhr/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1879. 两个数组最小的异或值之和

状态压缩+记忆化深搜

 

暴力搜索 

不能加 @lru_cache(None),会报错:

class Solution:
    def minimumXORSum(self, nums1: List[int], nums2: List[int]) -> int:
        # @lru_cache(None)
        def dfs(i, used):
            if i == len(nums1):
                return 0
            res = inf
            for j in range(len(nums2)):
                if j not in used:
                    used.append(j)
                    res = min(res, (nums1[i] ^ nums2[j]) + dfs(i + 1, used))
                    used.pop()
            return res
        return dfs(0, [])

状压+ dfs

class Solution:
    def minimumXORSum(self, nums1: List[int], nums2: List[int]) -> int:
        @lru_cache(None)
        def dfs(i, state):
            if i == len(nums1):
                return 0
            res = inf
            for j in range(len(nums2)):
                if state & (1 << j) == 0:
                    res = min(res, (nums1[i] ^ nums2[j]) + dfs(i + 1, state | (1 << j)))
            return res
        return dfs(0, 0)

1994 好子集的数目

质数计数+状压

1601. 最多可达成的换楼请求数目

 

 力扣

力扣

回溯

class Solution:
    def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
        def dfs(i, c):
            if i == l:
                if degree.count(0) == n:#是否形成多个环
                    return c
                return 0
            res = dfs(i + 1, c)#不将i节点加入环中
            degree[requests[i][0]] += 1
            degree[requests[i][1]] -= 1
            res = max(res, dfs(i + 1, c + 1))#将i节点加入寻找最大值,加入后需要将出入度进行恢复
            degree[requests[i][0]] -= 1
            degree[requests[i][1]] += 1
            return res
        degree = [0 for i in range(n)]
        l = len(requests)
        return dfs(0, 0)

作者:qin-qi-shu-hua-2
链接:https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests/solution/python-hui-su-zhuang-ya-shuang-jie-jian-om8tm/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

状压

class Solution:
    def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
        ans = 0
        for mask in range(1 << len(requests)):
            cnt = mask.bit_count()
            if cnt <= ans:
                continue
            delta = [0] * n
            for i, (x, y) in enumerate(requests):
                if mask & (1 << i):
                    delta[x] += 1
                    delta[y] -= 1
            if all(x == 0 for x in delta):
                ans = cnt
        return ans

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests/solution/zui-duo-ke-da-cheng-de-huan-lou-qing-qiu-ae0e/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution:
    def maximumRequests(self, n: int, requests: List[List[int]]) -> int:
         #二进制遍历每一种情况,找到最大值
        res=0
        for i in range(1<<len(requests)):
            #优化时间
            ans=i.bit_count()
            if ans<res:
                continue
            degree=[0 for _ in range(n)]
            for j in range(len(requests)):
                #每种组合
                if i&(1<<j):
                    degree[requests[j][0]]+=1
                    degree[requests[j][1]]-=1
            #如果degree里都是0,则达到平衡
            flag=True 
            for h in range(n):
                if degree[h]!=0:
                    flag=False
            if flag:
                res=max(ans,res)
        return res

2151 基于陈述统计最多好人数

力扣

class Solution:
    def maximumGood(self, S: List[List[int]]) -> int:
        n,ans=len(S),0
        #检查状态是否合理
        def check(state):
            cnt = 0
            #检查每个说真话的人
            for i in range(n):
                if state>>i & 1:
                    for j in range(n):
                        state_j = state>>j & 1
                        if S[i][j] == 0 and state_j or S[i][j] == 1 and not state_j:
                            return False,cnt
                    cnt += 1
            return True,cnt
        #枚举每个状态
        for state in range(1,1<<n):
            #如果状态合理,统计一的个数
            res,cnt = check(state)
            if res:
                ans = max(ans,cnt)
        return ans

作者:tang-bo-o
链接:https://leetcode-cn.com/problems/maximum-good-people-based-on-statements/solution/mei-tian-yi-dao-kun-nan-ti-di-9tian-ji-y-3azc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution:
    def maximumGood(self, statements: List[List[int]]) -> int:
        #二进制枚举,n个玩家里好人的组合,根据statements判断是否符合条件
        def check(state):
            for j in range(len(statements)):
                for h in range(len(statements)):
                    #如果j是好人,h是好人,statements[j][h]=0不符合
                    #如果j是好人,h是坏人,statements[j][h]=1不符合
                    if j!=h:
                        if state&(1<<j) and state&(1<<h) and statements[j][h]==0:
                            return False
                        if state&(1<<j) and not state&(1<<h) and statements[j][h]==1:
                            return False
            return True
        res=0
        for i in range(1<<len(statements)):
            #这个状态是否满足statements
            if check(i):
                ans=i.bit_count()
                res=max(res,ans)
        return res

位运算的应用

体验优化,过滤的时候。

面试题 17.01. 不用加号的加法

 383,不使用“+”,“-”,“×”,“÷”实现四则运算

 

 

 

class Solution {
    public int add(int a, int b) {
        int x = a ^ b;
        int y = (a & b) << 1;

        while(y != 0){
            int temp = x ^ y ;
            y = (x & y) << 1;
            x = temp;
        }

        return x;
    }
}

作者:so-what-u
链接:https://leetcode-cn.com/problems/add-without-plus-lcci/solution/di-gui-huo-xun-huan-by-so-what-u-g7yg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

有符号二进制的加法减法

二进制数的加法和减法:有符号二进制数的加减法_微学苑

136. 只出现一次的数字

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        a=0
        for num in nums:
            a=a^num
        return a

693 交替位二进制数

宫页题解

6065. 按位与结果大于零的最长组合

 二进制模拟

class Solution {
  public int largestCombination(int[] candidates) {
    int[] cnt = new int[32];
    int max = 0;
    for (int c : candidates) {
      for (int i = 0; i < 32; i++) {
        if (((1 << i) & c) > 0) cnt[i]++;
      }
    }
    for (int i = 0; i < 32; i++) {
      max = Math.max(max, cnt[i]);
    }
    return max;
  }
}

作者:hu-li-hu-wai
链接:https://leetcode.cn/problems/largest-combination-with-bitwise-and-greater-than-zero/solution/by-hu-li-hu-wai-6kum/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6105. 操作后的最大异或和

 可以操作任意次,所以是,可以看成每个数,可以选无数次,像完全背包那样。

每一位有没有出现过1,只要出现过结果就是1。

异或性质:有1个是1,结果就是1.

class Solution(object):
    def maximumXOR(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        def maxlen(k):
            flag = False
            for num in nums:
                if num & (1 << k):
                    flag = True
                    break
            return flag

        res = 0
        for i in range(32):
            # 遍历二进制位
            cur = maxlen(i)
            if cur:
                res |= (1 << i)
        return res

268. 丢失的数字

 

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int ans = 0;
        for (int i = 0; i < nums.size(); i++) 
            ans ^= nums[i];
        for (int i = 0; i <= nums.size(); i++) 
            ans ^= i;
        return ans;
    }
};

作者:bianchengxiong
链接:https://leetcode.cn/problems/missing-number/solution/acmjin-pai-ti-jie-ha-xi-wei-yun-suan-bia-mwtz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6186. 按位或最大的最小子数组长度

 位运算+动态规划

class Solution(object):
    def smallestSubarrays(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        #从后往前遍历
        #用res记录32位每个1的最近出现位置
        ans = [0 for _ in range(len(nums))]
        res = [0 for _ in range(32)]
        for i in range(len(nums)-1,-1,-1):
            cur = nums[i]
            #当前的nums[i]涵盖了哪些32位
            for j in range(32):
                if cur & (1 << j):
                    res[j] = i 
            need_max = i
            for j in range(32):
                need_max = max(need_max,res[j])
            ans[i] = need_max - i + 1
        return ans

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值