P1036 [NOIP2002 普及组] 选数
[NOIP2002 普及组] 选数
题目描述
已知 \(n\) 个整数 \(x_1,x_2,\cdots,x_n\),以及 \(1\) 个整数 \(k\)(\(k<n\))。从 \(n\) 个整数中任选 \(k\) 个整数相加,可分别得到一系列的和。例如当 \(n=4\),\(k=3\),\(4\) 个整数分别为 \(3,7,12,19\) 时,可得全部的组合与它们的和为:
\(3+7+12=22\)
\(3+7+19=29\)
\(7+12+19=38\)
\(3+12+19=34\)
现在,要求你计算出和为素数共有多少种。
例如上例,只有一种的和为素数:\(3+7+19=29\)。
输入格式
第一行两个空格隔开的整数 \(n,k\)(\(1 \le n \le 20\),\(k<n\))。
第二行 \(n\) 个整数,分别为 \(x_1,x_2,\cdots,x_n\)(\(1 \le x_i \le 5\times 10^6\))。
输出格式
输出一个整数,表示种类数。
样例 #1
样例输入 #1
4 3
3 7 12 19
样例输出 #1
1
提示
【题目来源】
NOIP 2002 普及组第二题
2.题解
2.1 子集枚举
思路
概念请参考子集枚举
难点集中在如何确定n个数中选取k个数的问题,这里将n个数的下标组成一个二进制数
通过函数__builtin_popcount(){注意该函数前面的下划线是两个,后面的下划线是一个!!!}可以判断一个二进制数有几位是1来判断是否满足选取了k个数。
至于范围选定就是从[0,11111...111]即可,如何快速获得n个1? (即 (1 << n) - 1, 可参考链接中的介绍)
代码
// 子集枚举
#include<bits/stdc++.h>
using namespace std;
bool isPrime(int num){
if (num <= 1) return false;
if (num <= 3) return true;
// 若num不是质数,num = a * b, a > sqrt(num),则必有 b < sqrt(num), 我们只要看能否找到这个b即可判断了
for(int i = 2; i <= sqrt(num); i++){
if(num % i == 0) return false;
}
return true;
}
int main(){
int n, k;
int ans = 0;
cin >> n >> k;
vector<int> arr(n);
for(int i = 0; i < n; i++){
scanf("%d", &arr[i]);
}
int U = 1 << n; // 得到全集,确定范围
for(int S = 0; S < U; S++){ //枚举所有子集[0,U)
int sum = 0;
if(__builtin_popcount(S) == k){
for(int i = 0; i < n; i++){
if(S & (1 << i)) sum += arr[i];
}
if (isPrime(sum)) ans++;
}
}
cout << ans;
}

浙公网安备 33010602011771号