位运算

进制转换

提到位运算,不得不说的是进制的概念。我们在日常生活中使用的往往是十进制,但是在计算机中却是以二进制存储信息。同样常用的有八进制和十六进制。

如何进行进制转换呢?
首先是从十进制转换成N进制:对这个十进制数进行mod(N)运算直到结果为0,再将得到的模数反过来输出就是结果。
例如,将十进制的6转换成二进制,6%2=0(6/2=3),3%2=1(3/2=1),1%2=1(1/2=0),逆序输出即为110
其次是从N进制转换成十进制:对于N进制数的第i位数字,它所代表的就是这位数字乘以N的(i-1)次幂。
例如,六进制的521转换成十进制, 160+261+562=193

练习1 进制转换

先输入一个N,和一个N进制的数X,再输入一个M,要求将N进制的数字X转换成M进制并输出结果。题目保证N和M小于10。

进阶:如果题目中的N和M小于16呢?小于36呢?
(提示:在十六进制中,A-F分别代表10-15)

#include<bits/stdc++.h>
using namespace std;

int Trans_to_demical(int n, string num) {
    int ans = 0;
    int xs = 1;
    int sz = num.size();
    for(int i = sz - 1; i >= 0; i --) {
        if(num[i] >= 'A') ans += (num[i] - 'A' + 10) * xs;
        else ans += (num[i] - '0') * xs;
        xs *= n;
    }
    return ans;
}

string Trans_to_M(int m, int num) {
    string res = "";
    while(num > 0) {
        char pl = 0;
        int tmp = num % m;
        if(tmp >= 10) pl = tmp - 10 + 'A';
        else pl = tmp + '0';
        res = pl + res;
        num /= m;
    }
    return res;
}

void solve(int n, string num, int m) {
    cout << Trans_to_M(m, Trans_to_demical(n, num)) << endl;
}

int main() {
    int n, m;
    string num;
    cin >> n >> num >> m;
    solve(n, num, m);
    return 0;
}

位运算

左移操作相当于乘以2,右移操作相当于除以2并向下取整。

位运算在信息学竞赛中有着很多的应用。

异或操作在竞赛中是一个很重要的考点,这个操作有一个很重要的性质,异或操作的逆运算是他的本身。

也就是说a ^ b ^ b == a。

练习2 签到系统

学校新引进了一批签到系统,要求每个人在课前和课后都在系统中填写自己的学号,已知学号的范围是1~1e9,某次课程只有一名学生逃课,请你用最快的一种方法判断出哪名同学逃课了?
先输入一个N,N <= 1000000,接下来N个数代表每个人的学号,保证数据中只有一个数出现了1次,其余数字都出现两次。

样例输入:5 1 2 3 2 1
样例输出:3

运用以上性质,每输入一个数就参与到异或运算,最后的结果就是只出现一次的数字。

#include<bits/stdc++.h>
using namespace std;

int main() {
    int n, tmp;
    cin >> n;
    int res = 0;
    for(int i = 0; i < n; i ++) {
        scanf("%d", &tmp);
        res ^= tmp;
    }
    cout << res << endl;
    return 0;
}

位运算的一些小应用

如何快速判断一个整数是奇数还是偶数?

x&1;

如何快速去掉一个整数的最后一位二进制?

x>>1;

如何在二进制最后加一个零?如何加一个一?

x<<1;
x<<1|1;

如何把右数第k位变成1?如何取右数第k位的数?

x|(1<<(k-1));
x|~(1<<(k-1));

如何把末尾k位都变成1?

x|((1k)-1);

如何把最后一个1变成0?

x&(x-1);

如何计算一个整数里面有多少个1?

int cnt=0;
while(x){
    x=x&(x-1);
    cnt++;
}

如果要将A变成B,需要改变多少位的二进制位?

x=A^B;
int cnt=0;
while(x){
    x=x&(x-1);
    cnt++;
}
练习3 逃跑路线

有一个3 * 4矩阵迷宫,小明位于左下角的A点,出口在右上角的B点,我们规定向上走为1,向右走为0,则请求出小明都有什么方案可以逃出迷宫。例如0010101就是一种方案。

从0000111枚举到1110000,输出所有共有3位是1的二进制数。

#include<bits/stdc++.h>
using namespace std;

string Trans_to_M(int m, int num) {
    string res = "";
    while(num > 0) {
        char pl = 0;
        int tmp = num % m;
        if(tmp >= 10) pl = tmp - 10 + 'A';
        else pl = tmp + '0';
        res = pl + res;
        num /= m;
    }
    int sz = res.size();
    for(int i = 0; i < 7 - sz; i ++) res = "0" + res;
    return res;
}

int main() {
    int tot = 0;
    for(int i = 7; i <= 112; i ++) {
        int tmp = i;
        int cnt = 1;
        while(tmp = tmp & (tmp - 1)) cnt ++;
        if(cnt == 3) cout << Trans_to_M(2, i) << endl, tot ++;
    }
    cout << "共有" << tot << "种方法" << endl;
    return 0;
}

信息学中的位运算

位运算在信息学竞赛中最常见的操作除了用来简化操作,最重要的就是进行状态压缩,模拟集合的运算。

一个unsigned int型的数有32个二进制位,所以最多可以表示一个包含32个元素的集合,每一位上的1代表这一位上代表的元素存在,为0则不存在。

假设有两个集合A和B,并集运算为A | B,交集运算为A & B,加入第i个元素为A = A | (1 << i),删除第i个元素为A = A | ~(1 << i)。

练习4 等式密码

现在有形如1( )2( )3( )4( )5( )6( )7( )8( )9 == N的等式,我们要在括号里填入+、-和*,不考虑运算符的优先级,即1+2*3 == 9而不等于7,求一共有多少种填法。

思路一:设+为0,-为1,*为2,枚举从00000000到22222222的三进制数。

#include<bits/stdc++.h>
using namespace std;

int calc(int num, int op, int nex) {
    if(op == 0) return num + nex;
    else if(op == 1) return num - nex;
    else if(op == 2) return num * nex;
}

void solve(int n) {
    for(int i = 0; i < 6561; i ++) {
        int num = i;
        int op[10] = {0};
        for(int j = 8; j >= 1; j --) {
            op[j] = num % 3;
            num /= 3;
        }
        int res = 1;
        for(int j = 1; j <= 8; j ++) {
            res = calc(res, op[j], j + 1);
        }
        if(res == n) {
            for(int j = 1; j <= 8; j ++) {
                cout << j << " ";
                if(op[j] == 0) cout << "+ ";
                else if(op[j] == 1) cout << "- ";
                else if(op[j] == 2) cout << "* ";
            }
            cout << "9 == " << n << endl;
        }
    }
}

int main() {
    int n;
    cin >> n;
    solve(n);
    return 0;
}

思路二:深搜。

#include<bits/stdc++.h>
using namespace std;

int op[10];

void dfs(int res, int pos, int n) {
    if(pos == 10) {
        if(res == n) {
            cout << "1 ";
            for(int i = 2; i <= 9; i ++) {
                if(op[i] == 1) cout << "+ ";
                else if(op[i] == 2) cout << "- ";
                else if(op[i] == 3) cout << "* ";
                cout << i << " ";
            }
            cout << "== " << n << endl;
        }
        return;
    }
    op[pos] = 1;
    dfs(res + pos, pos + 1, n);
    op[pos] = 2;
    dfs(res - pos, pos + 1, n);
    op[pos] = 3;
    dfs(res * pos, pos + 1, n);
}

int main() {
    int n;
    memset(op, 0, sizeof(op));
    cin >> n;
    dfs(1, 2, n);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值