做洛谷p1036 选数 ,对子集枚举,二进制的感想

老规矩上链接:https://www.luogu.com.cn/problem/P1036

题干比较长,我口述一下:

给定n个数字,从这n个数字内部选取k个数字,使得这k个数字之和为素数,这样的数就算一组。

在给定n,k的情况下,我们要求一共有多少组这样的数字。

4646abd5352d4a0c8d86893547801c1f.png

这道题的思路是这样的,我们把n看作一个集合内的所有元素个数,叫做集合U。

我们枚举这个集合U的所有子集。

然后在所有子集中找到:有k个元素的子集A(这样的子集有很多种)。

 然后我们再计算子集A内部元素是否位质数。

如果是,答案加一就可以了。

下面是代码的实现:

集合U的表示:

假如集合U内的元素有五个,

     U:(1)   ( 2)    (3)    (4 )  ( 5)

              12           13        14          15        16

              

子集A:   12                       14                       16

我们将子集A内的元素和全集内的元素对应起来,出现了则标记为1

               1           0            1             0           1  ——————21

全集U     1           1             1             1           1————————37

这样的话,我们就可以用不同的值来代表每一个子集,因为集合没有重复元素,且每一位与二进制的每一位一一对应;

全集U的建立 :int u=1<<(n-1)

子集A的遍历:for(int s=0;s<=u;s++)

 

检测出现的元素是否有k个,就看二进制内1的个数是否为A;

有两种检测方法:

 

第一个是一个函数: __builtin_popcount(变量名字) 这个函数会返回该变量代表的数的二进制内的1的个数;

 

第二个方法:(自己构造一个函数)(建议背下这个模板,记不住自己推一下)

int count_the_one(int x)
{
	int bns = 0;
	while (x)
	{
		x = x & (x - 1);
		bns++;
	}
	return bns;
}

第三步:

查找子集内部元素:

if (__builtin_popcount(s) == k)
//if(count_the_one(s)==k)
{
	int sum = 0;
	for (int i = 1; i <= n; i++)
	{
		if (s & (1 << (i-1)))
		{
			sum += arr[i];
		}
	}
	if (check(sum))ans++;
}

1<<(i-1)表示的是第几个出现的元素,第1个元素出现了则为 1<<(1-1),第二个元素则为1<<(2-1) =11

 

s&(1<<(i-1))表示的是第i个出现的元素是否与子集s有交集,也就是子集s内是否含有第i个元素

 

第四步

累加子集内部元素:

经过查找后,我们只需要每次sun+=arr[i]就行了。

 

最后一步:筛素数:

bool check(int x)
{
	for (int i = 2; i * i <= x; i++)
	{
		if (x % i == 0)return false;
		
	}
	return true;
}

只要sum为素数,答案++;

 

大功告成!!!!上代码:

int arr[30];
int count_the_one(int x)
{
	int bns = 0;
	while (x)
	{
		x = x & (x - 1);
		bns++;
	}
	return bns;
}

bool check(int x)
{
	for (int i = 2; i * i <= x; i++)
	{
		if (x % i == 0)return false;
		
	}
	return true;
}
int main()
{
	int n,k,ans=0;

	cin >> n >> k;
	for (int i = 1; i <=n; i++)scanf("%d", &arr[i]);
	int u = (1 << n)-1;
	for (int s = 0; s <= u; s++)
	{
		//if (__builtin_popcount(s) == k)
		if(count_the_one(s)==k)
		{
			int sum = 0;
			for (int i = 1; i <= n; i++)
			{
				if (s & (1 << (i-1)))
				{
					sum += arr[i];
				}
			}
			if (check(sum))ans++;
		}
	}
	cout << ans;

}


点点赞啊,这篇题解写了我好久好久的

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

louisdlee.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值