二进制状态枚举

定义

先给出子集的定义:子集是一个数学概念:如果集合A的任意一个元素都是集合B的元素,那么集合A称为集合B的子集。

用途

在写程序的时候,有时候我们可能需要暴力枚举出所有的情况,这时可以考虑通过二进制来枚举子集来尝试解决问题。

解释

假设我们现在有5个小球,上面分别标号了0,1,2,3,4代表这些小球的权值,现在要像你求出这些小球的权值可以组成的所有情况。

我们用二进制的思维来考虑这个问题,因为有5个小球,所以我们用5个比特位来分别标记小球存在还是不存在,对于这样一种情况,比如我们现在要选择3个小球,分别是0,3,4号小球,那么我们用二进制1表是当前的小球存在,用0表示当前小球不存在

二进制下标 4 3 2 1 0
二进制 1 1 0 0 1
小球状态 存在 存在 不存在 不存在 存在

我们可以用5个比特位来表示这种情况,如果小球全部选择的话那么二进制表示就是11111,二进制的11111转化为十进制数字就是31,这个数字正好就是2^5−1,那么我们可以用从0~(2^5−1)这些数表示完所有的选取状态(因为这个范围内的二进制数情况正好包括了这些选取状况).

代码实现

用代码实现非常简单,具体看注释

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n=5;//5个小球
    for(int i=0; i<(1<<n); i++) //从0~2^5-1个状态
    {
        for(int j=0; j<n; j++) //遍历二进制的每一位
        {
            if(i&(1<<j))//判断二进制第j位是否存在
            {
                printf("%d ",j);//如果存在输出第j个元素
            }
        }
        printf("\n");
    }
    return 0;
}

/*运行结果
0
1
0 1
2
0 2
1 2
0 1 2
3
0 3
1 3
0 1 3
2 3
0 2 3
1 2 3
0 1 2 3
4
0 4
1 4
0 1 4
2 4
0 2 4
1 2 4
0 1 2 4
3 4
0 3 4
1 3 4
0 1 3 4
2 3 4
0 2 3 4
1 2 3 4
0 1 2 3 4
*/ 

例题

例题可以做一下HDU-5616

HDU5616 Jam’s balance(二进制枚举子集)

一道习题

看如何用二进制枚举:

话说大诗人李白,一生好饮。幸好他从不开车。

一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:

无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。

这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。 

请你计算李白遇到店和花的次序,可以把遇店记为1,遇花记为0

public class 李白打酒二进制 {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int ans = 0;
		for (int i = 0; i < (1<<14); ++i) {
		    int tot_1 = 0;//tot_1遇到店的次数
		    int tot_0 = 0;//tot_0遇到花的次数
		    int num = 2;
		    for (int j = 0; j < 14; ++j) {//从0开始,到13,一共14位;查看每一位是0还是1
		    	//j为0时,i&1-->查看的第一位;j为1时i&10-->查看第二位...以此类推
		        if ((i&(1<<j))==1) { // 这里判断二进制 i 从右数第 j + 1 位是否为 1
		            tot_1++;
		            num = num * 2;
		        } else {
		            tot_0++;
		            num = num - 1;
		        }  
		    }
		    if (tot_1 == 5 && tot_0 == 9 && num == 1) {//最后一次结果已知,判断前14次即可
		        ++ans; // 记录合法方案数
		    }
		}
	}
 
}

猜你喜欢

转载自blog.csdn.net/tingtingyuan/article/details/81221639