位运算的使用

位运算的概述

我们知道,计算机中的数在内存中都是以二进制形式进行存储的 ,而位运算就是直接对整数在内存中的二进制位进行操作,因此其执行效率非常高,在程序中尽量使用位运算进行操作,这会大大提高程序的性能。

那么,涉及位运算的运算符如下表所示:

另外还有 >>> 它的行为于普通的右移运算符类似.

对于正数,>> 和 >>>效果相同 

但是对于负数,>> 和 >>> 效果不一样

>> 是在左边用符号位补位

>>> 是在左边都是用0补位

位运算的优先级

优先级需要弄清楚,如果不太清楚可以加小括号确保是想要的运算顺序,这里只是相对优先级,即只是和一些常用的算术运算符做比较

负数的位运算

首先,我们要知道,在计算机中,运算是使用的二进制补码,而正数的补码是它本身负数的补码则是符号位不变,其余按位取反,最后再 + 1得到的, 例如:

15 原码:00001111  补码:00001111 

−15,原码:10001111补码:11110001

那么对于负数的位运算而言,它们的操作都是建立在补码上的,得到的运算结果是补码,最后将补码结果转化成一个普通的十进制数结果。但需要注意的是,符号位是需要参与运算的,而在左移右移操作中,负数右移补1左移右边补0。例如对于 −15 ,其补码为11110001 , 右移一位(−15 >>1)得到的是11111000 ,即−8,其他的同理

注意:-1的补码为 11111111, 按位取反就变为00000000,这实际上就是0。

运算的应用

位运算实现乘除法

拓展异或

异或的性质

和常见的位运算性质

位运算习题

交换两个值

这效率非常高,我们来剖析其原理,对于a = a ∧ b则 b = b ∧ ( a ∧ b ) 根据交换律以及异或性质,得b = b ∧ b ∧ a = 0 ∧ a = a 同理 a = ( a ∧ b ) ∧ a = 0 ∧ b = b 这样就实现了交换操作。

比较两个值的大小

先记录a和b的差值,再通过c的符号来判断a和b的大小(因为如果c为负数,则c的二进制补码首位为1,如果是非负数则是0)

找到缺少的数字

ac代码:

本题使用了异或的第四条性质

找出只出现一次的数组

数组中一种数出现了奇数次,其他的数都出现了偶数次,找到出现了奇数次的数

本题这个性质

找出出现了奇数次的2种数

数组中有2种数出现了奇数次,其他的数都出现了偶数次,返回这2种出现了奇数次的数

最后的ac代码:

找回出现少于m次的数

数组中只有1种数出现次数少于m次,其他数都出现了m次,返回出现少于m次的数

思路:

最后的ac代码:

关于异或

将 n 个数任意分成若干组,定义每个组的权值为该组所有数的异或和,求最小权值之和

思路:

由于 a ⊕ b < = a + b所以任意两个数,不分组比分组更优,即所有数全在一个组时最优。

最后的ac代码:

判断一个整数是不是2的幂

思路:首先该整数不能是负数。

           当该整数大于0时,只有当该整数的二进制数里只有一个1

所以通过Brian Kernighan算法,提取出最右侧的1的状态,如果它和原本的状态相等,则说明它是2的幂。

最后的ac代码

找到大于等于n的最小的2某次方

已知n是非负数,返回大于等于n的最小的2某次方

思路:

以下是模拟实现

最后再+1遍可以求得答案

最后的ac代码

返回所有数字 & 的结果

求区间【left,right】内所有数字 & 的结果

思路:

最后的ac代码

01背包,但是bit

本题表面是个01背包,但是对于放入物品的体积的计算是进行按位或运算,所以本题其实是位运算题。

对于涉及到位运算的题一定要把数字当成二进制串来看

并且不存在进位意味着:位于位之间是不相互影响的。

思路:

设m的第x位为1

设选中的物品的wi做或运算的结果是sum

如果sum的第x位为0,那么第x位之后的那些位就是任意的

因此我们去枚举这个第x位,m的第x位要为1

而选择的物品的第x位是0,并且选择物品的高位应该是m的高位的子集合

就是wi(高位) | m == m

低位随意

代码:

# include <stdio.h>
struct node
{
	int v;
	int w;
};

int max(int a, int b)
{
	if (a > b)
		return a;
	else 
		return b;
}

int main()
{
	int t;
	scanf("%d", &t);
	int n, m;
	int ans;
	int ans1;
	int mx;
	struct node a[10];
	while (t)
	{
		ans1 = 0;
		ans = 0;
		mx = 0;
		scanf("%d %d", &n, &m);
		for (int i=0; i<n; ++i)
			scanf("%d %d", &a[i].v, &a[i].w);
		for (int i=0; i<n; ++i)
			if ((a[i].w | m )== m)
				ans = ans + a[i].v; //特殊情况,wi和m相同,或者是m的子集
		for (int i=31; i>=0; --i)
		{
			if (m >> i & 1 ) //确保第x位为1
			{
				ans1 = 0;
				mx=(((m>>(i-1))<<i)|(1<<i)-1); //此处可以动手画一画,就是把第x位变为0,低位全                    
                                                                                   部变为1
				for (int j=0; j<n; ++j)
					if ((mx | a[j].w )== mx)
						ans1 = ans1 + a[j].v;
				ans = max(ans1, ans);
			}
		}
		printf("%d\n", ans);
		t = t - 1;
	} 
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值