Gym - 102267K Birthday Puzzle (状态压缩)(遍历子集的位运算)

本文介绍了一种解决特定组合问题的方法,即计算一个整数集合所有非空子集的异或和。通过状态压缩技巧,利用位运算实现对所有子集的遍历并计算其异或和,适用于数据规模较小的情况。

参考来源

博客:
https://blog.youkuaiyun.com/qq_43333395/article/details/96397197#commentBox

题目链接

http://codeforces.com/gym/102267/problem/K

题意

给你一个集合,问它的所有子集(不为空)的元素或了之后 的和是多少。

理解

既然要求所有的子集的异或和,而这里n值又很小,O(2n)不会超时,所以直接遍历所有的子集,取或,求和,即可得到答案。

状态压缩挺暴力的,数据量比较大的时候,就要根据题意,排除不合法的方案,使一行的总方案数大大减少从而减少枚举

一些操作

以下代码能够输出它的所有子集,如果不理解可以用这输出看一下。

外层循环 i 的值为 1 到 2n ,代表了所有取的情况(不取零个),从取一个元素 到 取所有元素,并且每一种取法(取x个)对应的所有方案都存在,做到不重不漏。

内层循环 j 遍历一遍数组,用 i & (1 << j ) 判断要取哪几个

for (int i = 1; i < (1 << n); i++)
{
    for (int j = 0; j < n; j++)
     if (i & (1 << j))
     {
         cout<<a[j]<<" ";
         //cout<<"i:"<<i<<" j:"<<j;
     }
     cout<<endl;
 }

代码

较慢的代码,但挺好理解

#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
ll a[25];
ll cnt, num = 0;//这里要记得long long
void solve(int n)
{
    for (int i = 0; i < (1 << n); i++)
    {
        cnt = 0;
        for (int j = 0; j < n; j++)
            if (i & (1 << j))
                cnt = cnt | a[j];//某个子集内的所有元素取或
        num += cnt;//求和
    }
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);

    solve(n);
    printf("%lld\n", num);
    return 0;
}

另一份代码,用了更多的位运算,速度上会更快。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[25];
int main()
{	
	int n;
	scanf("%d", &n);
	for(int i = 0;i < n;i++)
	{
		scanf("%d", &a[i]);
	}
	
	int all = (1<<n);//2^n 
	int pos, t, tt;
	long long res = 0;
	
	for(int i = 1 ;i < all;i++)//遍历所有情况
	{
		pos = 0, t = 0, tt = i;
		while(tt)
		{
			if(tt & 1)
				t |= a[pos];
			tt >>= 1;
			pos++;
		}
		res += t;
	}
	printf("%lld\n", res);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值