暴力枚举刷题4

题目来源:组合的输出 - 洛谷
参考书目:《深入浅出程序设计竞赛(基础篇)》

解题思路:通过位运算来枚举数组a的所有子集,直到找到r的子集,按从小到大输出。解题思路很好理解,问题是怎么用位运算来枚举数值a的所有子集。具体思路如下:

1 、S = (1 << n) - 1:

  1 << n:这是位运算中的左移操作,意味着数字 1 在二进制形式下向左移动 n 位。例如,如果 n = 3,那么 1 << n 的结果是 1000(二进制),即 8(十进制)。

  - 1:从 1 << n 的结果中减去 1。继续上面的例子,8 - 1 等于 7,在二进制中表示为 111

因此,int S = (1 << n) - 1; 实际上是设置 S 为一个二进制数,该数由 n1 组成,代表一个大小为 n 的集合的全集,其中每个 1 可以看作是集合中一个元素的存在。

也就是说第一次for 循环的目的是枚举一个大小为 n 的集合的所有可能的子集,从最大的全集(所有元素都存在)到空集(没有元素)。

2、 检查每个子集的大小

   (1)内层循环 for (int i = 0; i < n; i++):这个循环遍历 S 的每一位。循环中的 i 代表集合中元素的位置索引。

   (2)位运算检查 if (S & (1 << i)):这里使用了位与运算(&)来检查 S 中的第 i 位是否为 1。具体来说,(1 << i) 生成了一个只在第 i 位为 1 的数,其他位都是 0。当这个数与 S 进行位与运算时,只有当 S 的第 i 位也为 1 时,结果才不为 0,即 if 条件为真。这表示集合中的第 i 个元素在当前子集 S 中。

3、记录和计数:每当发现第 i 个元素在子集中时(即 if (S & (1 << i)) 为真),代码就会将 i 存入数组 a 中,并且计数器 cnt 会增加 1。数组 a 用来记录当前子集中所有元素的索引,而 cnt 则表示当前子集中元素的数量。

4、检查子集大小:外层循环的每次迭代结束时,通过 if (cnt == r) 检查当前子集的大小是否等于 r。如果等于,那么就以特定的格式输出该子集中元素的索引(逆序并调整为从 1 开始计数)。

#include<iostream>
#include<cstdio>


using namespace std;

int a[30];

int main()
{
	int n, r;
	cin >> n >> r;
	//从全集枚举到0
	for (int S = (1 << n) - 1; S >= 0; S--) {  //从n个1一直枚举到n个0
		int cnt = 0;
		for (int i = 0; i < n; i++)
		{
			if (S & (1 << i))
				a[cnt++] = i;
		}
		if (cnt == r)//找到子集,逆序输出这个子集的元素
		{
			for (int i = r - 1; i >= 0; i--)
			{
				printf("%3d", n - a[i]);
			}
			puts(" ");
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值