NOIP 2002 普及组第二题 “选数” 题解
题目描述
已知 (n) 个整数 (x1x_1x1, x2x_2x2, \dots, 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≤n≤20,k<n(1 \leq n \leq 20, k < n(1≤n≤20,k<n)。
第二行 (n) 个整数,分别为x1x_1x1, x2x_2x2, …\dots…, xnx_nxn(1≤xi≤5∗106)(1 \leq x_i \leq 5 *10^6)(1≤xi≤5∗106)。
输出格式
输出一个整数,表示种类数。
示例
输入
4 3
3 7 12 19
输出
1
题目大意
给定n个数,从中选出k个数,求和为质数有多少种情况
1≤n≤20,k<n1 \leq n \leq 20, k < n1≤n≤20,k<n
1≤xi≤5∗1061 \leq x_i \leq 5 *10^61≤xi≤5∗106
分析
考虑到1≤n≤20,k<n1 \leq n \leq 20, k < n1≤n≤20,k<n,本题有两种思路:
① 使用深度优先搜索
② 使用限制数量的子集枚举
解析
挺水的一道题,还是挺简单的
DFS
本题的数据范围只有20,所以用DFS不会超时。
本题可以看作是一个限制数量的子集枚举,并对每一种可能性求和。首先定义vis数组,表示xix_ixi是否出现过。接着定义void dfs(), 并附带一个int m和long long sum。由于本题还要求合,所以光有一个递归层数m是不行的,需要再加上一个sum变量。在每次的递归中,首先确定递归边界,当m==km == km==k即已经选了k个数时,要判断和是否为质数并记录答案。接着依次遍历xix_ixi,记录这个解,进行下一次递归,并撤销这个解。
所以代码就是:
int n, k, ans = 0, a[25];
bool vis[25];
bool isPrime(long long x) {
if (x < 2) return false;
for (long long i = 2; i * i <= x; ++i) {
if (x % i == 0) return false;
}
return true;
}
//m 递归次数
//sum 当前的和
void dfs(int m, long long sum) {
if (m == k && isPrime(sum)) {
++ans;
return;
}
for (int i = 0; i < n; ++i) {
if (vis[i]) {
return;
}
vis[i] = 1;
dfs(m + 1, sum + a[i]);
vis[i] = 0;
}
}
运行后,可以看到正常输出了111😃
限制数量的子集枚举
没啥好说的,套模板就行
bool isPrime(long long x) {
if (x <= 2) {
return false;
}
for (int i = 2; i * i <= x; ++i) {
if (x % i == 0) {
return false;
}
}
return true;
}
int n, k, ans = 0;
cin >> n >> k;
vector<int> a(n + 2);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
long long sum = 0;
for (int i = 0; i < (1 << n); ++i) {
for (int j = 0; j < n; ++j) {
if (i & (1 << j) && __builtin_popcount(i) == k) {
sum += a[j];
}
}
if (__builtin_popcount(i) == k) {
if (isPrime(sum)) {
++ans;
}
sum = 0;
}
}
cout << ans;
3701



被折叠的 条评论
为什么被折叠?



