老规矩上链接:https://www.luogu.com.cn/problem/P1036
题干比较长,我口述一下:
给定n个数字,从这n个数字内部选取k个数字,使得这k个数字之和为素数,这样的数就算一组。
在给定n,k的情况下,我们要求一共有多少组这样的数字。
这道题的思路是这样的,我们把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;
}
点点赞啊,这篇题解写了我好久好久的