1.位运算:https://www.cnblogs.com/yrjns/p/11246163.html
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
Tips:
(1)&与相当于求交集操作,|相当于求并集操作
(2)位运算中的& | 不同于判断条件中的与和或,也就是if中的条件句要写成a&&b 或 a||b
(3)注意运算的优先级:不知道优先级就加括号就行!优先级:https://baike.baidu.com/item/%E8%BF%90%E7%AE%97%E7%AC%A6%E4%BC%98%E5%85%88%E7%BA%A7/4752611?fr=aladdin
2.状压操作:
(1)取出整数n在二进制表示下的第k位:(n>>k)&1,将n右移k位得到第k位的数值
(2)取出整数n在二进制表示下的第0~k-1位:n&((1<<k)-1),2^k-1应当表示从0~k-1为所有都为1,高于k位表示0的数,与之and操作取出0~k-1范围内的1
(3)把整数n的第k为取反:n^(1<<k),将第k位赋成1,如果n的第k位为1,则1xor1=0,1->0,如果为0,0xor1=1,0->1;
(4)把n的第k位赋成1:n|(1<<k)强行将2^k并入n中
(5)把n的第k位赋成0:n&(~(1<<k)) 将第k位赋成0,等价于将2^k的补集与n求交集,也就是~(1<<k)与n求交集
(6)判断n中是否有连续的1:(n&(n<<1))||(n&(n>>1)) 分别左移右移判断有无真!
(7)查询n中的第i位是0or1:n&(1<<k)
(8)将n直接按为取反(保留原有的位数不变): n^=((1<<k)-1)右边的数表示从0-k-1位全为真,和n取反也就是将每一位都取反1
3.例题:最短Hamilton路径
给定一张n个节点的带权无向图,点从0到n-1编号,要求从0到n-1的最短的Hamilton路径。Hamilton路径指的是从0道n-1不重不漏地经过每一个点恰好一次
Brute_force?枚举n个点的全排列,按照给定排列尝试,复杂度:O(n*n!)
可以用状压DP压缩到O(n*2^n)
首先,我们如何表示那些节点已经经过,而那些节点还未被经过?我们可以用一个n位的二进制数表示!对于每一位,如果为0则表示尚未经过,1则表示这一位下的节点已经经过了
在任意时刻我们还需要知道当前所在的位置,因此设f[i][j]表示当前经过路径信息为i时,当前位置到j的最短Hamilton路径。其中i∈[0,2^n-1],j∈[1,n]
在起点0处时,初始化f[1][0]=0;对于剩下所有的状态,初始化为一个极大值,目标结果是f[2^n-1][n-1]
我们可以得到转移方程:f[i][j]=min{f[i^(1<<j)][k]+weight(j,k)}; 其中转移前的状态必定是没有j的,然后任选一个中间节点k作为转移信息
其中条件是:(i&(1<<j) )&& (i^(1<<j))&(1<<k) or (i>>j)&1=1 且k属于[0,n] 因为j恰好被经过一次,所以一定是有上一个没有j的状态中转移过来的,则上一个状态的j位应该被赋值为0.同时上一时刻所处的位置是
i xor (1<<j)中任意一个是1的数位k,从而推导出上式。
原文地址:https://www.cnblogs.com/little-cute-hjr/p/12289749.html