算法刷题中的小技巧——位运算

基本运算方式

二进制下:

0 & 0 = 0	0 & 1 = 0

1 & 0 = 0	1 & 1 = 1

二进制下:

0 | 0 = 0     0 | 1 = 1

1 | 0 = 0     1 | 1 = 1
!0 = 1(true)	!x = 0(false)	

x为所有非零数。

异或

二进制下:

0 ^ 0 = 0	1 ^ 1 = 0

1 ^ 0 = 1	0 ^ 1 = 1
取反

二进制下:

~01001 = 10110
移位

二进制下:

原数:1000011

左移一位:<< 1	变为:0000110(高位溢出)

右移一位:>> 1	变为:0100001(低位溢出)

基本性质

1. 异或
交换律
a ^ b = b ^ a
结合律
a  ^ (b ^ c) = (a ^ b) ^ c
无分配律
2. a + b >= a ^ b
证明:a + b = 2(a & b) + a ^ b

运用方式(例题)

二进制中1的个数
题目描述

给定一个长度为n的整数数组a,你需要求出每个元素的二进制表示中1的个数。

输入

第一行:一个整数n;
第二行:n个整数,表示数组a。

输出

共一行,输出n个整数,其中第i个数为a[i]的二进制中1的个数。

题目分析 / 实现思路

本题我们可以用&将a[i]与1进行比较。如果二进制下的a[i]最后一位为1,那么a[i] & 1的值为true,反之便为false。在进行了该运算后我们便可将 a[i]右移一位,进行下一轮的运算,直到a[i]的值为零。这样我们便可求出a[i]二进制下1的个数。

代码实现
#include <bits/stdc++.h>
using namespace std;
int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 加速cin / cout
    int n;cin >> n;
    for (int i = 0;i < n;i++)
    {
        int x, res = 0;cin >> x;
        while (x) if (x & 1) res++, x >>= 1; else x >>= 1;
        cout << res << ' ';
    }
    return 0;
}

注:使用Day3中讲到的bitset可以直接做。

我们需要0
题目描述

给定一个大小为n的非负整数数组a。
你可以选定一个非负整数x,并令b[i] = a[i] ^ x,请问是否存在x,使得b[1] ^ b[2] ^ … ^ b[n] = 0?

输入

第一行:一个整数t,代表有t个案例,对于每个案例:

第一行:一个整数n(奇数),表示数组大小;
第二行:n个整数,表示数组a。

输出

对于每个案例:若存在符合条件的x,则输出x;否则输出-1。

题目分析 / 实现思路

原式为:

(a1 ^ x) ^ (a2 ^ x) ^ ... ^ (an ^ x) = 0

利用结合律可将括号拆开,并使用交换律,得原式等价于:

a1 ^ a2 ^ ... ^ an ^ x ^ .. ^ x = 0

化简得:

a1 ^ a2 ^ ... ^ an ^ x = 0

要求得x,我们只需实现其逆运算即可。

即:

x = a1 ^ a2 ^ ... ^ an

代码实现
#include <bits/stdc++.h>
using namespace std;
int main()
{
	int t;cin >> t;
	while (t--)
	{
		int n;cin >> n;
		long long res = 0;
		for (int i = 0;i < n;i++)
		{
			int a;cin >> a;
			res ^= a;
		}
		cout << res << '\n';
	}
	return 0;
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值