【算法】二进制,状态压缩专题

在算法竞赛中,二进制运算因其高效性和简洁性被广泛应用于优化时间复杂度、空间复杂度以及代码实现。持续更新中…


一、二进制基础运算规则

1. 基本位运算符

运算符符号定义(按位操作)示例(二进制)
&全1为1,否则为01010 & 1100 = 1000
``有1为1,全0为0
~取反(注意补码符号位)~1010 = 0101(假设4位)
异或^相同为0,不同为11010 ^ 1100 = 0110

2. 移位运算

操作符号定义示例(二进制)
左移<<低位补0,高位溢出丢弃1010 << 2 = 101000
逻辑右移>>>高位补0,低位溢出丢弃(无符号数)1010 >>> 2 = 0010
算术右移>>高位补符号位(有符号数)1010 >> 2 = 1110(假设4位补码)

3. 运算优先级

优先级从高到低:~<<, >>&^|
注意:建议使用括号明确优先级,避免混淆。


二、算法竞赛高频套路

1. 位掩码(Bitmask)

  • 用途:用整数二进制位表示集合状态(如子集、开关状态)。
  • 示例:枚举集合 {a, b, c} 的所有子集:
	// 枚举所有子集
	vector<int> nums = {1, 2, 3};
	int n = nums.size();
	for (int mask = 0; mask < (1 << n); mask++) {
	    vector<int> subset;
	    for (int i = 0; i < n; i++) {
	        if (mask & (1 << i)) {
	            subset.push_back(nums[i]);
	        }
	    }
	    // 处理 subset
	}

2. 快速判断与操作

  • 奇偶判断x & 11为奇数,0为偶数。
  • 最低位的1lowbit = x & -x(如 12 (1100) → 4 (100))。
  • 判断2的幂(x & (x-1)) == 0(如 8 (1000) → True)。

3. 异或(XOR)魔法

  • 交换变量a ^= b; b ^= a; a ^= b;(无需临时变量)。
  • 找唯一数:数组中其他数出现两次,找唯一出现一次的数:
    a^a=0 ,b^0=b
	int singleNumber(vector<int>& nums) {
	    int res = 0;
	    for (int num : nums) res ^= num;
	    return res;
	}
  • 消除重复x ^ x = 0x ^ 0 = x

4. 状态压缩动态规划

  • 场景:用二进制位表示状态(如旅行商问题TSP、棋盘覆盖)。
  • 示例:TSP问题中记录已访问城市的状态:
  dp[mask][i] = 最短路径,其中 mask 表示已访问的城市集合,i 表示当前位置。

5. 位运算优化

  • 快速乘除2的n次幂:对正数 x << n 等价于 x * 2^nx >> n 等价于 x // 2^n
  • 快速幂算法:利用二进制分解指数:
ll mod_pow(ll a, ll b, ll mod) {
    ll res = 1;
    while (b) {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

6. 统计二进制中1的个数

		1.统计二进制中 1 的个数
		int count = __builtin_popcount(x);       // 32位整数
		int count = __builtin_popcountll(x);     // 64位整数
		
		2. ​前导零(Leading Zeros)​
		int leading_zeros = __builtin_clz(x);     // 例如 x=1 (0001) → 31(32位下)
		int leading_zeros = __builtin_clzll(x);  // 64位版本
		
		3. ​末尾零(Trailing Zeros)​
		int trailing_zeros = __builtin_ctz(x);   // 例如 x=8 (1000) → 3

7. 格雷码生成

  • 二进制转格雷码gray = x ^ (x >> 1)
    G i = B i + 1 ⊕ B i G_i = B_{i+1} \oplus B_i Gi=Bi+1Bi

8. 函数

__lg() 函数是一个非标准的函数,只在部分编译器中支持,它返回一个整数值,表示输入参数的二进制表示中最高位1的位置(从0开始计数)。例如,__lg(8)将返回3,因为8的二进制表示是1000,最高位1在第3位。

log2() 函数是C++11标准中提供的函数,它返回一个浮点数值,表示以2为底的对数。例如,log2(8)将返回3.0,因为8等于2的3次方。它们的返回值类型不同,前者返回整数,后者返回浮点数。另外,__lg()是非标准函数,只在部分编译器中支持,而log2()是C++11标准库中的函数,在大部分编译器中都可以使用。

__builtin_clz() 前导零统计函数

__builtin_clz(unsigned int x)        //用于无符号整数
int__builtin_clzl(unsigned long x)        //用于无符号长整数
int__builtin_clzll(unsigned long long x)        //用于无符号长长整数

//求最高有效位的位置
unsigned msb(unsigned x)
{
    return sizeof(unsigned) * CHAR_BIT - 1 - __builtin_clz(x);
}
//功能上等价于
__lg(x)
floor(log2(x))

__builtin_popcount() 统计二进制中为1的位数的函数

__builtin_popcount (unsigned int)        //用于无符号整数

三、注意事项与陷阱

  1. 位移溢出:左移超过数据类型位数时结果未定义(如 1 << 100 在32位整数中会溢出)。
  2. 符号位问题:右移有符号数时使用算术右移(负数补1),可能导致意外结果。
  3. 优先级混淆a & b == c 实际等价于 a & (b == c),需用括号明确优先级。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值