2020東北林業大學acm集訓第五天 二進位制操作與二進位制列舉(附相關習題及AC程式碼)

小菜菜李發表於2020-12-29


一、二級制操作(算數位運算)

1.與(&):

兩個數A與B
A = 60 (0011 1100)
B = 13 (0000 1101)
則A&B (0000 1100)
也就是對二進位制每一位進行一次與操作若兩個數同為1,結果為1,否則結果為0

2.或(|):

兩個數A與B
A = 60 (0011 1100)
B = 13 (0000 1101)
則A | B(0011 1101)
對二進位制每一位進行一次或操作兩個數中只要有一個數為1,結果為1,否則為0

3.非(~):

數字A:(1111 0000)
則~A: (0000 1111)
對二進位制每一位數字進行按位取反,若1則0,若0則1

4.異或(^):

兩個數A與B
A = 60 (0011 1100)
B = 13 (0000 1101)
則A^B (0011 0001)
對二進位制每一位數字來說,若數字相同,返回0,數字不同返回1
值得注意的是,異或運算滿足結合律,a ^ b ^ c <=> a ^ c ^ b
對同一個數字異或兩次,結果還是原值,也即任何數字對0進行異或運算,結果都為原值。 這一點是很重要的,很多習題都會用到。
注:異或運算也可以推廣到字元運算中,若兩個字元相同則返回0。

二、二進位制移位操作符

移位操作有兩種,分別是左移和右移。

1.左移(<<)

即二進位制中所有數字向左移一位,空白處用零補齊
如(1<<2)=(100)=4;
二進位制中,每左移一個單位,數字變為2倍。在c/c++中,左移運算速度快於乘法運算速度。

2.右移(>>)

與左移類似,右移就是所有數字向右移一位,超出位置的數字捨棄
如(100>>2)=1;
每右移一個單位,數字除以2(捨棄小數部分)。

三.二進位制列舉

我們知道,在二進位制中,每一位數字只有兩種情況,0或者1,而我們可以利用他的這種特性,來列舉各種情況,比如可以我們用1來代表存在,0代表不存在。
話不多說,直接上例題
在這裡插入圖片描述
如題,一共有n個數,從中隨機選出若干個數,使他們的和等於給定的K,求滿足要求的情況數。

我們假設n等於3,使用二進位制列舉,則需要創造出一個三位的二進位制數,第n位代表第n個數,用1表示選擇了這個數,0表示沒選擇這個數,那麼所有的情況就可以表示為從二進位制數000到111的列舉,設此二進位制數已經列舉到了 i 。

那麼問題來了,如何判斷第n位的數字是1還是0呢?
這時候我們可以使用前面講過的 “與運算” 和 “二進位制移位” 了,當我們判斷第j位數字是否為1時,可以這樣:

if (i&(1<<j))//當滿足這個條件時,說明第j位數字是1(從後往前數)
{
	balabalabala......
}

是不是有點蒙?
等等,我們慢慢想想…一共三個數字,需要取其中任意個數字使他們的和等於一個已知的數,於是我們用二進位制列舉,用1表示取了這個數,0表示沒取,於是所有的情況都包含在了從000到111的列舉過程中,這時我們要判斷第n位數字(從後往前數)是否為1,我們使用了判斷條件 i & (1<<j) , 這個的意思是,將1向左移 j 位,得到的二進位制數除了第j位以外都是0,與i做一次與操作,那麼如果i中第j位為1,則返回值為1,條件為真,執行if。
下面是完整程式碼:

#include <bits/stdc++.h>

using namespace std;
int x[30];

int main()
{
    int n;
    int k;

    while(cin>>n>>k)
    {
        int f=1;
        int ans=0;
        for(int i=0; i<n; i++)
            cin>>x[i];
        for(int i=0; i<(1<<n); i++)
        {
            ans=0;
            for(int j=0; j<n; j++)
                if(i&(1<<j))
                    ans+=x[j];
            if(ans==k)
            {
                cout<<"Yes"<<endl;
                f=0;
                break;
            }

        }

        if(f==1)
        {
            cout<<"No"<<endl;
        }
    }


    return 0;
}

四.習題部分

-----------------------------------------------------------------------------------------
林大OJ 643
teacher Li
Problem:A

Time Limit:1000ms
Memory Limit:65536K
Description
This time,suddenly,teacher Li wants to find out who have missed interesting DP lesson to have fun.The students who are found out will get strictly punishment.Because,teacher Li wants all the students master the DP algorithm.
However,Li doesn’t want to waste the class time to call over the names of students.So he let the students to write down their names in one paper.To his satisfaction,this time, only one student has not come.
He can get the name who has not come to class,but,it is troublesome,and,teacher always have many things to think about,so,teacher Li wants you, who is in the ACM team, to pick out the name.
Input
There are several test cases.The first line of each case have one positive integer N.N is the number of the students,and N will not greater than 500,000.
Then,following N lines,each line contains one name of students who have attended class.The N-1 lines are presented after N lines.These N-1 lines indicates the names of students who have come to class this time,one name in one line.
The length of student’s name is not greater than 30.
Process to the end of file.
Output
For each test case, first print a line saying “Scenario #k”, where k is the number of the test case.Then output the name of the student who have not come to class.One case per line.Print a blank line after each test case, even after the last one.
Sample Input
3
A
B
C
B
C
Sample Output
Scenario #1
A

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;
    char name1[33];
    char name2[33];
    int count=0;
    while(cin>>n)
    {
        count++;
        cin>>name1;
        for(int i=0; i<2*n-2; i++)
        {
            cin>>name2;
            int r1=strlen(name1);
            int r2=strlen(name2);
            for(int j=0; j<(r1>r2?r1:r2); j++)
            {
                name1[j]^=name2[j];
            }
        }
        cout<<"Scenario #"<<count<<endl;
        cout<<name1<<endl;
        cout<<endl;
    }
    return 0;
}

-----------------------------------------------------------------------------------------
林大OJ1172
Find different
Problem:B

Time Limit:20000ms
Memory Limit:5000K
Description
Give an odd number n, (1<=n<=10000001)

Given you an array which have n numbers : a[1], a[2] a[3] … a[n].They are positive num.
There are n/2 numbers which appear twice and only one number appears once.
Now you should tell me the number which appears only once.
Input
There are several test cases end with EOF.
The first line there is a integer n.
Then the 2nd line there are n integer a[1],a[2]…a[n].
Output
For each case, output the number which appears only once.
Sample Input
7
3 2 7 2 1 7 3
1
7
11
1 1 2 2 3 3 4 4 5 5 9
Sample Output
1
7
9
Hint
資料量較大,建議用scanf讀入

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n;int x;
    while(cin>>n)
    {
        int ans=0;
        while(n--)
        {
            cin>>x;
            ans=ans^x;
        }
        cout<<ans<<endl;
    }

    return 0;
}

-----------------------------------------------------------------------------------------
林大OJ1205
和為K–二進位制列舉
Problem:C

Time Limit:1000ms
Memory Limit:65535K
Description
給出長度為n的陣列,求能否從中選出若干個,使他們的和為K.如果可以,輸出:Yes,否則輸出No
Input
第一行:輸入N,K,為陣列的長度和需要判斷的和(2<=N<=20,1<=K<=10^9)
第二行:N個值,表示陣列中元素的值(1<=a[i]<=10^6)
Output
輸出Yes或No
Sample Input
5 13
2 4 6 8 10
Sample Output
No

#include <bits/stdc++.h>

using namespace std;
int x[30];

int main()
{
    int n;
    int k;

    while(cin>>n>>k)
    {
        int f=1;
        int ans=0;
        for(int i=0; i<n; i++)
            cin>>x[i];
        for(int i=0; i<(1<<n); i++)
        {
            ans=0;
            for(int j=0; j<n; j++)
                if(i&(1<<j))
                    ans+=x[j];
            if(ans==k)
            {
                cout<<"Yes"<<endl;
                f=0;
                break;
            }

        }

        if(f==1)
        {
            cout<<"No"<<endl;
        }
    }


    return 0;
}

-----------------------------------------------------------------------------------------
林大OJ1285
趣味解題
Problem:D

Time Limit:1000ms
Memory Limit:65535K
Description
ACM程式設計大賽是大學級別最高的腦力競賽,素來被冠以"程式設計的奧林匹克"的尊稱。大賽至今已有近40年的歷史,是世界範圍內歷史最悠久、規模最大的程式設計競賽。比賽形式是:從各大洲區域預賽出線的參賽隊伍,於指定的時間、地點參加世界級的決賽,由1個教練、3個成員組成的小組應用一臺計算機解決7到13個生活中的實際問題。
現在假設你正在參加ACM程式設計大賽,這場比賽有 n 個題目,對於第 i 個題目你有 a_i 的概率AC掉它,如果你不會呢,那麼這時候隊友的作用就體現出來啦,隊友甲有 b_i 的概率AC掉它, 隊友乙有 c_i 的概率AC掉它,那麼現在教練想知道你們隊伍做出 x 個題目的概率。
Input
輸入一個正整數T(T<=100),表示有T組資料,對於每組資料首先輸入一個 n (7<=n<=13),表示有 n 個題目,接下來輸入三行,
第一行輸入 n 個數a_i,第二行輸入 n 個數b_i,第三行輸入 n 個數c_i, 其中 a_i, b_i, c_i 的意義如題,最後輸入一個 x 表示教練想要知道你們隊伍做出的題目數(x>=0)。
Output
輸出一行表示結果,保留4位小數
Sample Input
2
7
0.1 0.2 0.3 0.4 0.5 0.6 0.7
0.2 0.3 0.4 0.5 0.6 0.7 0.8
0.3 0.4 0.5 0.6 0.7 0.8 0.9
1
7
0.1 0.2 0.3 0.4 0.5 0.6 0.7
0.2 0.3 0.4 0.5 0.6 0.7 0.8
0.3 0.4 0.5 0.6 0.7 0.8 0.9
5
Sample Output
0.0000
0.2811

Source
ITAK

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int t;
    cin>>t;double a[15];double b[15]; double c[15];
    while(t--)
    {
        int n;int x;
        cin>>n;


            for(int i=0;i<n;i++)
            cin>>a[i];
            for(int i=0;i<n;i++)
            cin>>b[i];
            for(int i=0;i<n;i++)
            cin>>c[i];
            cin>>x;

        double ans1=0;
        for(int i=0;i<(1<<n);i++)
        {
            double ans=1;int count=0;
            for(int j=0;j<n;j++)
            {
                if(i&(1<<j))
                {
                    ans*=a[j]+(1-a[j])*b[j]+(1-a[j])*(1-b[j])*c[j];
                    count++;
                }
                else ans*=(1-a[j])*(1-b[j])*(1-c[j]);
            }
            if(count==x)
            ans1+=ans;
        }
        printf("%.4lf\n",ans1);
    }

    return 0;
}

-----------------------------------------------------------------------------------------
林大OJ1505
陳老師加油-二進位制列舉
Problem:E

Time Limit:1000ms
Memory Limit:65535K
Description
陳老師經常開車在哈爾濱的大街上行走,假設剛開始油箱裡有T升汽油,每看見加油站陳老師就要把汽油的總量翻倍(就是乘2);每看見十字路口氣油就要減少1升;最後的時候陳老師的車開到一個十字路口,然後車就沒油了------就熄火了,陳老師好痛苦啊~~~!
然後他就開始回憶,一路上一共遇到5個加油站,10個十字路口,問造成這種慘烈的境遇有多少種可能?

Input
輸入一個T ,(1<=T<=100);
Output
輸出可能的方案數。
Sample Input
1
Sample Output
10

#include <bits/stdc++.h>

using namespace std;


int main()
{
    int t;
    while(cin>>t)
    {
        int count1=0;
        for(int i=0;i<(1<<15);i++)
        {
            int cross=0;int add=0;int tmp=t;
            for(int j=0;j<15;j++)
            {
                if(i&(1<<j))
                {
                    add++;
                    tmp*=2;
                }
                else
                {
                    cross++;
                    tmp--;
                    if(tmp==0)
                    break;
                }
            }

            if(add==5&&cross==10&&tmp==0)
                count1++;
        }
        cout<<count1<<endl;
    }

    return 0;
}

-----------------------------------------------------------------------------------------
林大OJ1641
權利指數
Problem:F

Time Limit:1000ms
Memory Limit:65535K
Description
在選舉問題中,總共有n個小團體,每個小團體擁有一定數量的選票數。如果其中m個小團體的票數和超過總票數的一半,則此組合為“獲勝聯盟”。n個團體可形成若干個獲勝聯盟。一個小團體要成為一個“關鍵加入者”的條件是:在其所在的獲勝聯盟中,如果缺少了這個小團體的加入,則此聯盟不能成為獲勝聯盟。一個小團體的權利指數是指:一個小團體在所有獲勝聯盟中成為“關鍵加入者”的次數。請你計算每個小團體的權利指數。
Input
輸入資料的第一行為一個正整數T,表示有T組測試資料。每一組測試資料的第一行為一個正整數n(0<n<=20)。第二行有n個正整數,分別表示1到n號小團體的票數。
Output
對每組測試資料,在同一個行按順序輸出1到n號小團體的權利指數。
Sample Input
2
1
10
7
5 7 4 8 6 7 5
Sample Output
1
16 22 16 24 20 22 16
Hint
HDU

#include <bits/stdc++.h>

using namespace std;
int  x[30];
int y[30];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        memset(y,0,sizeof(y));
        int n;
        cin>>n;
        double total=0;
        for(int i=0; i<n; i++)
        {
            cin>>x[i];
            total+=x[i];
        }
        total/=2;
        for(int i=0; i<(1<<n); i++)
        {
            int  sc=0;
            for(int j=0; j<n; j++)
            {
                if(i&(1<<j))
                {
                    sc+=x[j];

                }
            }
            if(sc>=total)
            {
                for(int j=0; j<n; j++)
                {
                    if(i&(1<<j))
                    {
                        if((sc-x[j])<total)
                        y[j]++;
                    }
                }
            }
        }
        for(int i=0; i<n-1; i++)
        {
            cout<<y[i]<<" ";
        }
        cout<<y[n-1];
        cout<<endl;
    }
    return 0;
}

這題千萬記得初始化!!
-----------------------------------------------------------------------------------------
林大OJ1518
紙牌遊戲-二進位制-搜尋
Problem:G

Time Limit:1000ms
Memory Limit:65535K
Description
給你一些撲克,每張都對應一個點數,分別對應1-13,K 就是13;J 是11;Q是12;
現在想從這些撲克牌中取出一些牌,讓這些牌的點數的和等於一個幸運數值P,問有多少種方案?
Input
輸入資料第一行為n和p,分別代表n張撲克牌和幸運數(1<=n<=20,p<=260)
接下來是這n張牌的點數; 1<=點數<=13;
Output
輸出能得到P 的方案數?
Sample Input
5 5
1 2 3 4 5
Sample Output
3

#include <bits/stdc++.h>

using namespace std;
int x[25];

int main()
{
    int n,p;
    while(cin>>n>>p)
    {
        int count1=0;
        for(int i=0;i<n;i++)
            cin>>x[i];
        for(int i=0;i<(1<<n);i++)
        {
            int ans=0;
            for(int j=0;j<n;j++)
            {
                if(i&(1<<j))
                ans+=x[j];
            }
            if(ans==p)
            {
                count1++;
            }
        }
        cout<<count1<<endl;
    }

    return 0;
}

碼字不易,所有程式碼均本人手打,親測AC,給個贊吧0.0
作者水平有限,不足之處歡迎指出。

相關文章