暴力枚举相关问题---利用二进制枚举法

1. 李白喝酒问题

题目描述:

话说大诗人李白,一生好饮。幸好他从不开车。一天,他提着酒壶,从家里出来,酒壶中有酒两斗。他边走边唱:

               1. 无事街上走,提壶去打酒。

               2. 逢店加一倍,遇花喝一斗。

这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。请你计算李白遇到店和花的次序,有多少种可能得方案。

题目解析:

1. 此题求的是总的可能方案,利用dfs暴力枚举,是经常见到的方案,但是会产生很多辅助空间的浪费。

2. 利用二进制枚举是一个不错的方案,因为总共才遇店5次,遇花10次。将遇店看成二进制中的1,遇花看成二进制中的0。可以枚举出14位二进制来表示。

3.判断思路即为二进制中是否有9个0,5个1,并且最终正好剩余酒1斗。

代码如下:

int ans = 0; // 合法方案数
for(int i=0; i<(1<<14); ++i)
{
	int tot_1 = 0;
	int tot_0 = 0;
	int num = 2;
	for(int j=0; j<14; ++j)
	{
		if(i&(1<<j))
		{
			// 判断i代表的二进制中从右数第j+1位是否为1
			tot_1++;
			num = num*2;
		}
		else
		{
			tot_0++;
			num = num-1;
		}
	}
	if(tot_1 == 5 && tot_0 == 9 && num==1)
		++ans;
}

2. 任意数之和为X的方案数。

题目描述:

某君有n个互不相同的正整数,现在他要从这n个正整数之中无重复的选取任意个数,并仅通过加法凑出整数X。求某君有多少种不同的方案来凑出整数X。其中,两个整数n,X(1<= n <= 20,1<=X<=2000),并且输入的每个整数不超过100。

题目解析:

1. 拿到此题,第一反应的暴力是求所有数的组合数,然后对每个组合判断。

2. 但是求所有数的组合数最常见的是利用递归。这个缺点就是辅助内存。

3. 可以利用二进制枚举。由题意看出,n最多为20个,那个利用20位的二进制数表示,1表示选取第i个数,0表示不选。

代码如下:

#include <iostream>

using namespace std;

int main() {
int n,X,*a;
int plan=0;
cin>>n>>X;
a=new int[n];
for(int i=0;i<n;i++)
{
	cin>>a[i];
}
for(int b=0;b<(1<<n);b++)
{
    int sum = 0;
    for(int j=0; j<n; ++j)
    {
        if(b&(1<<j)) sum +=a[j];
    }
    if(sum == X)
        ++plan;
}
	cout<<plan;
}

3. 买玩具满足最多小朋友需求的人数

题目描述:

幼儿园有n个小朋友,每个小朋友都有自己想玩的玩具。现在要买一批玩具,经费有限。只能买m个玩具。已知商店一共卖k种玩具,编号为1,2,3……k,你让小朋友把想玩的编号都写在了纸上。你希望尽可能满足多的小朋友的需求,请你计算最多能满足多少个小朋友的玩具需求。

输入格式:

第一行,n,m,k(1 <= n <= 100,1 <= m <= k <= 15),中间用空格分开。

接下来n行,第i+1行的第一个数字ai代表第i个小朋友想玩的玩具数量,接下来有ai个数字,代表这个玩具的编号。

输出格式:

输出一个整数,代表最多能满足多少个小朋友的玩具需求。

题目解析:

1. 看到此题的玩具总数k不超过15。应当可以想到依然使用二进制枚举的方法,设置k位二进制数,第i位为1代表第i+1个玩具买了,为0代表第i+1个玩具不买。

2. 然后先判断当前二进制数,中为1的是否为m。在比较满足的小朋友个数,最后挨个比较完后,就可以求出最大满足小朋友个数。

代码如下:

#include<iostream>
#include<vector>
#include<math.h>

using namespace std;

int main()
{
	//count代表每个孩子想玩的玩具数量,toies代表每个孩子的想玩哪一种玩具
	int n, m, k, count[101], toies[101][15];
	cin >> n >> m >>k;
	for(int i=0; i<n; ++i)
	{
		int x = 0;
		cin >> x;
		count[i] = x;
		for(int j=0; j<x; ++j)
		{
			cin >> toies[i][j];
		}
	}
	// 开始计算
	int maxnum = 0;
	int curnum = 0;
	for(int i=0; i<(1<<k); ++i) // 利用二进制来代表选择哪几种玩具
	{
		int num = 0;
		curnum = 0;
		for(int j=0; j<k; ++j)
		{
			if(i&(1<<j))
				++num;
		}
		if(num != m)
			continue;
		// 判断能够满足几个孩子的需求
		for(int j=0; j<n; ++j)
		{
			bool bReachNeed = true;
			for(int h=0; h<count[j]; ++h)
			{
				if((i&(1 << toies[j][h]-1)) == 0)
				{
					bReachNeed = false;
					break;
				}
			}
			if(bReachNeed)
				++curnum;
		}
		if(curnum > maxnum)
			maxnum = curnum;
	}
	cout << maxnum << endl;
	return 0;
}

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值