一、二级制操作(算数位运算)
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
作者水平有限,不足之处欢迎指出。