二进制枚举子集

二进制枚举利用的是二进制下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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值