二进制枚举利用的是二进制下n位长度的数有
2
n
2^{n}
2n个,一个有n个元素的集合子集个数也为
2
n
2^{n}
2n个,所以可以利用二进制的1,0和集合中的元素联系起来
他可以实现组合也可以实现容斥
对一个二进制来说1代表取这个元素,0代表不取这个元素,1和0所在的位置代表元素的位置,这样的思想在有时候给题目有了很大的方便
我们来举一个例子点我看题
思路
我们用二进制枚举的方式来模拟答对k道题的概率,首先先要判断一下这个数的二进制下1的个数是不是有k个,当有k个时我们再利用这个二进制数进行概率上的乘法,我们认为1为答对的概率,0为答错的概率,利用用位运算&可以直接判断在j这一位是不是1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
double a[3][15];
double num[15];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=0;i<3;i++)
for(int j=0;j<n;j++)
scanf("%lf",&a[i][j]);
int k;
scanf("%d",&k);
for(int i=0;i<n;i++)
num[i]=1-(1-a[0][i])*(1-a[1][i])*(1-a[2][i]);
double sum=0;
for(int i=1;i<(1<<n);i++)
{
int x=i;
int cnt=0;
while(x)
{
if(x%2==1)
cnt++;
x/=2;
}
double ans=1;
if(cnt==k)
{
for(int j=0;j<n;j++)
if(i&(1<<j))
{
ans*=num[j];
}
else
ans*=(1-num[j]);
sum+=ans;
}
}
printf("%.4lf\n",sum);
}
return 0;
}
再来看一道题HDU 1557
思路
可以用二进制来枚举几个团体的票数之和,若票数之和大于总票数的一半,枚举元素,若为是关键参与者就加加
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long ll;
int a[25];
int b[25];
int vis[25];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
int n;
scanf("%d",&n);
int sum=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
for(int i=0;i<(1<<n);i++)
{
int s=0;
memset(vis,0,sizeof(vis));
for(int j=0;j<n;j++)
if(i&(1<<j))
{
s+=a[j];
vis[j]=1;
}
if(s>sum/2)
for(int j=0;j<n;j++)
if(vis[j]==1&&s-a[j]<=sum/2) b[j]++;
}
for(int i=0;i<n;i++)
i!=n-1?printf("%d ",b[i]):printf("%d\n",b[i]);
}
return 0;
}
二进制枚举还可以来实现容斥
int sum = 0;
//i的范围是1-2^p.size(),空集除外,每一个子集所对应的
//二进制都不一样,也就是i
for (int i=1; i<(1<<p.size()); ++i){
int mult = 1,bits = 0;
for (int j=0; j<p.size(); ++j)
if (i&(1<<j)){//与i的二进制的第j位比较,看是否为1,是则选中
bits++;//计算i中1的个数,也就是质因数的个数
mult *= p[j];
}
int cur = r / mult;
if (bits & 1)//若1的个数是奇数则进行加法,否则进行减法
sum += cur;
else sum -= cur;
}