题解:P1036 [NOIP 2002 普及组] 选数

思路

由于数据小并且难度是橙题、标签是搜索,考虑暴力搜索每种可能并判断总和是否为素数。

详细一点(定义 $m$ 为选的数个数,$s$ 为当前的和,$x$ 为下一个选的数的位置,$dfs(m, s, x)$ 为搜索函数,$ans$ 为种类数即题目所求):

  1. 确定当前选择的数;
  2. 从 $x$ 遍历 $i$ 至整个数组 $a$,每次搜索 $dfs(m + 1, s + a_i, i + 1)$;
  3. 当 $m = k$ 了,判断 $s$ 是否为素数:
    • 是:$ans + 1$;
    • 不是:结束搜索,因为后面没有继续搜索的必要。

代码

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
long long n, k, ans, a[25];
bool check(long long x) // 判断 x 是否为质数
{
    for (long long i = 2; i * i <= x; i++) // 避免使用 sqrt() 函数,减少精度误差
    {
        if (x % i == 0) // 等同于 x % i == 0
            return false;
    }
    return true;
}
void dfs(long long m, long long s, long long x) // m:选的数个数,s:当前的和,x:下一个选的数的位置
{
    if (m == k)
    {
        if (check(s)) // 如果当前的和是质数,则 ans++
            ans++;
        return;
    }
    for (long long i = x; i < n; i++) // 从 x 开始
        dfs(m + 1, s + a[i], i + 1);
    return;
}

int main()
{
    cin >> n >> k;
    for (long long i = 0; i < n; i++)
        cin >> a[i];
    dfs(0, 0, 0); // 初始状态:选的数个数为 0,当前的和为 0,下一个选的数的位置为 0
    cout << ans << endl;
    return 0;
}