位運算

進制轉換

提到位運算,不得不說的是進制的概念。我們在日常生活中使用的往往是十進制,但是在計算機中卻是以二進制存儲信息。同樣常用的有八進制和十六進制。

如何進行進制轉換呢?
首先是從十進制轉換成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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章