大家好这里是TKLA…初学算法的大一同学~现在已经系统学习过了C语言,正在学习C++以及python…希望能在CSDN上收获到很多知识!也希望自己能成为分享知识的一员!
目前学习的教材是这本 《算法竞赛进阶指南》(李煜东 著)
这本书按照 0x
加上两个16进制数字组成,还是比较有趣的
位运算是效率最高的运算模式,充满了技巧性。
位与 | 位或 | 位反 | 位异或 |
---|---|---|---|
& | | | ~ | ^ |
其中,与运算只要一端为0,结果为0,或运算只要一端为1,结果为1
异或运算当两端相异返回1否则为0
这里介绍一下思路一致的逻辑短路(原书没有)
当运算式由或、与决定,直接返回结果,而不会看另一端
譬如,true||false
只需要看左边一端即可判断整个式子
利用这一点,我们可以卡出许多有趣的操作~
leetcode 剑指offer 64
int sumNums(int n) {
int ans = n;
(n) && (ans+=sumNums(n - 1));
return ans;}
这里使用的就是逻辑短路以替代 if 语句
…毕竟递归出口还是要有 if
的
请大家无论如何都要去看看力扣上的大神题解!受益真的很大!
这个就略去啦!不过其中有提到一个语句
memset(a,0x3f,sizeof(a));
很有用…(初始化容器)
1 <<= n 等价于2n
n <<= 1 等价于2n
>>右移运算的向下取整机制
C语言中对于int / 2
采取向0取整而不是向下取整…所以说最好是使用位运算或者是floor函数(
acwing此题
typedef long long ll;
int power(int n,int m,int mod){
int ans = 1 % mod;
for(;m;m>>=1){
if(m & 1)
ans = ll(ans)*n%mod;
n = ll(n)*n%mod;}
return ans;}
用一张图解释下叭!
其中,可以观察到m >>= 1
以及m & 1
均为位运算表达,提升了效率
在这里总结一下几个
(x & 1) //奇数
!(x & 1)//偶数
x >>= 1
x &= (x-1)
//最低位的1归0
x &= -x
//得到最低位的1
x & (~x) == 0
交换数字
a ^= b ^= a ^= b;
n xor n == 0
n xor 0 == n
通过第二条性质可以推出异或前缀
:
int ans_1 = arr[0],ans_2 = ans_1;
for(int m=0;m<i-1;++m)
ans_1 ^= arr[m];
for(int m=0;m<j;++m)
ans_2 ^= arr[m];
cout << (ans_1 xor ans_2);
也即,arr[i]^...^arr[j]
可以分成两个部分来求arr[0]^...^arr[i-1]
以及arr[0]^...^arrr[j]
,再两式异或。
在结构上有着前缀和特征,因此被叫做前缀和性质
来做一道相关的leetcode
acwing此题
typedef long long ll;
ll mul(ll a,ll b,ll mod){
ll ans = 0;
for(;b;b>>=1){
if(b & 1)
ans = (ans+a)%mod;
a = 2*a%mod;}
return ans;}
#define elif else if
typedef long long ll;
typedef long double ld;
ll mul(ll a,ll b,ll mod){
a %= mod,b %= mod;
ll c = ld(a)*b/mod;
ll ans = a*b - c*mod;
if(ans<0)
ans += mod;
elif(ans>=mod)
ans -= mod;
return ans;}
这种解法…有点巧妙 放原文好了…
利用 a * b mod p = a * b - [a * b / p] * p , [ ]表示向下取整
首先,当 a,b < p 时,a * b / p 下取整以后一定也小于 p。我们可以用浮点数执行 a * b / p 的运算,而不用关心小数点之后的部分。浮点类型
long double
在十进制下的有效数字有 18 ~ 19 位,足够胜任。当浮点数的精度不足以保存精确数值时,它会像科学计数法一样舍弃低位,正好符合我们的要求。另外,虽然**a * b** 和 [a * b / p] * p 可能很大,但是两者的差一定在 0 ~ p - 1 之间,我们只关心它们较低的若干位即可。所以,我们可以用
long long
来保存**a * b 和 [a * b / p] * p**各自的结果。整数运算溢出相当于舍弃高位,也正好符合我们的要求。
将一个长度为m的bool数组使用m位二进制存储
操作 | 运算 |
---|---|
取出n的第k个bit | ( n >> k ) & 1 |
取出n的后k个bit | n & ( ( 1 << k ) - 1 ) |
取反n的第k个bit | n xor ( 1 << k) |
将n的第k个bit置为1 | n | (1 << k) |
将n的第k个bit置为0 | n & ( ~ ( 1 << k) ) |
acwing此题
是一个用位运算优化复杂度的例子…
我觉得有点绕
int f[1<<20][20];//用于标记,以int 压缩 bool*
int hamilton(int n,int weight[20][20]){
memset(f,0x3f,sizeof(f));
f[1][0] = 0;
for(int i=1;i<1<<n;++i)
for(int j=0;j<n;++j)
if(i>>j & 1)
//这个式子表示的意义:是否经过了当前状态对应的点
for(int k=0;k<n;++k)
if(i>>k & 1)
f[i][j] = min(f[i][j],f[i xor 1<<j][k]+weight[k][j]);
//i xor 1<
return f[(1<<n)-1][n-1];/*return经过所有点的状态*/}
相关题
邻接表里的
linked to(空链接)
n xor 1 = n + 1 , n & 1 == 0
n xor 1 = n - 1 , n & 1 == 1
lowbit(n)
指的是 最低位的1以及之后的0
这里简单说下,lowbit(n) = n & (~n + 1),而 ~n 其实就是 - (n+1) (补码)
linked to(空链接)
linked to(空链接)