二进制总结

 

常用操作

最右一位为第0位,从右到左依次为 0, 1, 2, 3……

取出n的第k位                              (n >> k) & 1

取出n的第0~k-1位                       n & ((1 << k) - 1)

n的第k位取反                              n ^ (1 << k)

n的第k位赋值为1                         n | (1 << k)

n的第k位赋值为0                         n & (~(1 << k))

判断奇偶:(二进制数以1结尾是奇数,以0结尾是偶数)

奇数&1==1

偶数&1==0

 

 

例题1

二进制状态压缩例题: 最短Hamliton路径

给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

题意:

1为起点,n为终点的最短汉密顿路径。数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]

思路:

二进制状态压缩中常用的各种位操作的含义:

取状态为sta,位置为 i 的数字:sta&(1<<i) 

把第 i 位设置为1:sta=sta|(1<<i)

把第 i 位设置为0:sta=sta&(~(1<<i) )

把第 i 位取反:sta=sta^(1<<i)

取出sta的最后一个1:(sta&-sta),运用补码表示的相关知识

用二进制上的数代表一个点的状态,取(1)或不取(0)。题目让求从点1到n的最短汉密顿路径,即经过每个点一次,这时的状态用二进制表示就是 (1<<n)-1 (n个1)。用dp[j][i]表示在状态 i 下,从1到 j 的最短汉密顿路径。

dp[j][i]可由上一个状态(上一状态就是把 j从当前状态中去掉)dp[k][i^(1<<(j-1))]得到,其中保证k是上一状态(i^(1<<(j-1)))中存在的点,即 i&1<<(k-1)==1。

有动态转移方程:dp[j][i]=min(dp[j][i],dp[k][i^(1<<(j-1))]+map[k][j])。

def Hamilton(matrix):
    maxn = 20
    dp = [[float('inf')] * maxn for i in range(1<<maxn)]
    dp[1][0] = 0
    for i in range(1, 1<<N):
        for j in range(N):
            if (i>>j) & 1 :
                for k in range(N):
                    if (i >> k) & 1:
                        dp[i][j] = min(dp[i][j], dp[i^(1<<j)][k] + matrix[k][j])
    return dp[(1<<N)-1][N-1]
'''
N = 4
matrix = [[0, 2, 1, 3], [2, 0, 2, 1], [1, 2, 0, 1], [3, 1, 1, 0]]
'''
N = int(input())
matrix = []
for i in range(N):
    matrix.append([int(k) for k in input().split(' ')])

例题2

lowbit问题

表示非负整数n在二进制下最低位1以及它后面的0构成的数值

比如n = 1010101000100

lowbit(n) = 100

lowbit(n) = n & (-n)

例题3

快速求二进制中1的个数

x & (x - 1) 可以把二进制中最后一位1去掉

比如10100变成10000

这里减去lowbit(x)也可以

int num(int x){ return !x ? 0 : 1 + num(x & (x - 1)); }

例题4

用异或来求配对的数

把数字从0,1开始两两配对,(0, 1), (2, 3), (4, 5)...以此类推。
每个数异或1就会得到跟它配对的数。
比如0 ^ 1 = 1, 1 ^ 0 = 04 ^ 1 = 5, 5 ^1 = 4

 

例题5

 快速幂取模 

SICAUæ°çèµ

当数字比较小的时候没问题,但是看到999999999时,肯定会超时。因此需要用到快速幂取模:

引理:积的取余等于取余的积的取余

a^b = (a*a)^(b/2)

具体分析参考: https://blog.csdn.net/DBC_121/article/details/77646508

# 实现快速幂取模
# 给定两个数A,B, 计算 A^B MOD (10^9 + 7)
# 引理:积的取余等于取余的积的取余

def quick_Exp_Mode(b, e, m):
    result = 1
    while e != 0:
        if (e & 1) == 1:   # 如果是奇数,将多出来的数乘上result
            result = (result * b) % m
        e >>= 1
        b = (b * b) % m    # 不断的两两合并再取模,减小a和b的规模
    return result

N = int(input())
List = []
for i in range(N):
    a, b = list(map(int, input().split(' ')))
    List.append(quick_Exp_Mode(a, b, pow(10, 9) + 7))

for i in List:
    print(i)

快速乘法取模算法

算a*b%p时,a,b,p在long long范围内,但a*b会炸long long

# 实现快速乘取模
# 给定两个数A,B, 计算 A*B MOD (10^9 + 7)

def qucik_Mul_Mod(x, y, m):
    result = 0
    while y != 0:
        if y & 1:   # 如果是奇数
            result = (result + x) % m
        x = x * 2 % m
        y >>= 1
    return result

N = int(input())
List = []
for i in range(N):
    a, b = list(map(int, input().split(' ')))
    List.append(qucik_Mul_Mod(a, b, pow(10, 9) + 7))

for i in List:
    print(i)

参考资料:

https://www.cnblogs.com/sugewud/p/9819345.html

还有一个链接忘记了,如果大佬看到了请见谅.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值