CSP历年复赛题-P1036 [NOIP2002 普及组] 选数
原题链接:https://www.luogu.com.cn/problem/P1036
题意解读:题目即要在n个数中,枚举出所有的子集,当子集中数字个数刚好为k时,求和,判断是否是素数。
解题思路:
方法一:二进制法
通过二进制法,可以枚举一个集合中所有元素“选”或者“不选”的情况,用二进制1表示选该元素,二进制0表示不选。
下面对样例数据进行模拟:
如上图,从上到下依次枚举0~24-1对应的二进制,根据二进制位的0/1来决定对应元素“选”或“不选”,
k=3的即二进制位有3个1的情况用绿色标识,求和后是素数的用红色标识。
因此对于一般的n个数,只需要枚举0~2n-1,判断每个数的二进制中1的个数是否为k,再针对每个二进制位是0或1决定选或不选对应元素。
这里就引出两个重要的二进制操作:
1、统计二进制有多少个1
首先,介绍一个关键的操作:lowbit,可以返回一个数的二进制中最后一个1所表示的整数
例如x=6,即二进制0110,lowbit(6) = 2,实现如下:
int lowbit(int x)
{
return x & -x;
}
要计算一个数的二进制中有多少个1,只需要每次都减去这个数的lowbit,直到0为止,统计次数:
int count1(int x)
{
int cnt = 0;
while(x)
{
x -= lowbit(x);
cnt++;
}
}
2、二进制的某一位是0还是1
设整数i,要判断第j个(最低位j=0)二进制位是0还是1,只需要判断i >> j & 1是0还是1
100分代码-二进制法:
#include <bits/stdc++.h>
using namespace std;
const int N = 25;
int n, k, ans;
int a[N];
//计算x的二进制中最后一个1所代表的整数,如11000回返回1000,也就是8
int lowbit(int x)
{
return x & -x;
}
//计算x的二进制中有多少个1
int count1(int x)
{
int cnt = 0;
while(x)
{
x -= lowbit(x);
cnt++;
}
return cnt;
}
//判断x是否是素数
bool isprime(int x)
{
if(x < 2) return false;
for(int i = 2; i * i <= x; i++)
{
if(x % i == 0) return false;
}
return true;
}
int main()
{
cin >> n >> k;
int pow2n = 1;
for(int i = 0; i < n; i++)
{
cin >> a[i];
pow2n *= 2; // 计算2^n
}
for(int i = 0; i < pow2n; i++)
{
if(count1(i) == k) //如果整数i的二进制中1的个数==k
{
int sum = 0;
for(int j = 0; j < n; j++) //遍历二进制的每一位
{
if(i >> j & 1) sum += a[j]; //i >> j & 1表示将i右移j位再和1求&,即判断i的第j位二进制是否是1,注意j=0表示第一位
}
if(isprime(sum)) ans++;
}
}
cout << ans;
return 0;
}
方法二:DFS
n个数字,每个数字只有选或者不选两种情况,可以用一个数组保存选择的状态,1表示选,0表示不选,通过DFS来填充此数组即可。
100分代码-DFS:
#include <bits/stdc++.h>
using namespace std;
const int N = 25;
int n, k, ans;
int a[N];
int tmp[N];
//判断x是否是素数
bool isprime(int x)
{
if(x < 2) return false;
for(int i = 2; i * i <= x; i++)
{
if(x % i == 0) return false;
}
return true;
}
void dfs(int level)
{
if(level > n) // 找到一组选择
{
//判断是否有k个1
int cnt = 0;
for(int i = 1; i <= n; i++)
{
if(tmp[i] == 1) cnt++;
}
if(cnt != k) return;
int sum = 0;
for(int i = 1; i <= n; i++)
{
if(tmp[i] == 1) sum += a[i];
}
if(isprime(sum)) ans++;
return;
}
for(int i = 0; i <= 1; i++)
{
tmp[level] = i;
dfs(level + 1);
}
}
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; i++) cin >> a[i];
dfs(1);
cout << ans;
return 0;
}