1. 程式人生 > 實用技巧 >二進位制列舉

二進位制列舉

讓我們從一個題目入手

從一個大小為n的整數集中選取一些元素,使得它們的和等於給定的值T。每個元素限選一次,不能一個都不選。

關於這個題目,我們很容易想到的便是對所有元素進行暴力搜尋,然後進行剪枝便可。

下面我將介紹二進位制列舉的思路和流程來巧妙的解決這個問題。

對任一數來說,所面臨的問題是取或不取,在二進位制中便可以用1或0來表示。由於每個數都會對應一串二進位制數字,那麼結果便有2^n個狀態,如1000001便相對於取0號位元素和最後一號元素,其他元素不取。那麼我們可以列舉這2^n個狀態,通過遍歷當前狀態的每一位來判斷,然後對取的元素求和判斷是否為一個合格的狀態。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
const int maxn = 2005;
int a[maxn], n, t, cnt;
int main()
{
	cin >> n;
	for(int i = 0; i < n; i++) cin >> a[i];
	cin >> t;
	for(int i = 1; i < (1 << n); i++)
    {
        int sum = 0;//元素和
        for(int j = 0; j < n; j++)
        {
            if(i & (1 << j))//判斷當前狀態i的二進位制數第j位是否為1,取或不取
                sum += a[j];
        }
        if(sum == t)//滿足條件記錄
        {
            for(int j = 0; j < n; j++)
            {
                if(i & (1 << j))
                    cout << a[j] << " ";
            }
            cout << endl;
            cnt++;//記錄合理數
        }
    }
    cout << cnt << endl;
}

看完上面程式碼,有些小夥伴就要問了,i&(1<<j)是什麼。

那麼補充一波位運算的知識吧:

按位與運算子(&)

參加運算的兩個資料,按二進位制位進行“與”運算。

運算規則:0&0=0; 0&1=0; 1&0=0; 1&1=1;

即:兩位同時為“1”,結果才為“1”,否則為0

例如:3&5 即 0000 0011& 00000101 = 00000001 因此,3&5的值得1。

左移運算(<<)

a << b就表示把a轉為二進位制後左移b位(在後面添b個0)。例如100的二進位制為1100100,而110010000轉成十進位制是400,那麼100 << 2 = 400。可以看出,a << b的值實際上就是a乘以2的b次方,因為在二進位制數後添一個0就相當於該數乘以2(這樣做要求保證高位的1不被移出)。
通常認為a << 1比a * 2更快,因為前者是更底層一些的操作。因此程式中乘以2的操作請儘量用左移一位來代替。注意:不要用於浮點數型別

最後,給出二進位制的基本模板

  	for(int i = 0; i < (1<<n); i++) //從0~2^n-1個狀態
    {
        for(int j = 0; j < n; j++) //遍歷二進位制的每一位
        {
            if(i & (1 << j))//判斷二進位制第j位是否存在
            {
                //進行操作
            }
        }
        printf("\n");
    }