洛谷 P1036. 选数 -- dfs搜索

[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 普及组第二题


这道题大概思路是想出来了 也想出了不降原则来去重 但还是没做出来 还是不够熟练
这道题有个关键点就是 不让选到的数重复 我们让dfs递归的时候循环从当前函数数的序号开始
比如 我们现在算数组中以第i个数开始的四个数的和能否是质数 那么接下来的一个数我们只能从i+1 i+2一直选到n 不能回头取选i前面的数

#include <bits/stdc++.h>

using namespace std;

const int N = 30;
int n, k, cnt;
int a[N];

bool is_prime(int x)
{
    if (x < 2) return false;
    for (int i = 2; i <= x / i; i ++ )
    {
        if (x % i == 0) return false;
    }
    return true;
}

void dfs(int m, int sum, int startx) //m表示当前已经选择了几个数 sum表示当前选择数的和 startx表示当前选择数的序号 保证是升序序列 避免重复 
{
    if (m == k)
    {
        if (is_prime(sum)) cnt ++ ;
        return;
    }
    else
    {
        for (int i = startx; i < n; i ++ ) //关键点!i从startx开始 保证是升序选取数组中元素 从而不会重复 
        {
            dfs(m + 1, sum + a[i], i + 1); //这里升序起始值要变成i+1,以免算重
        }
    }
    return;
}

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    dfs(0, 0, 0);

    printf("%d\n", cnt);

    return 0;
}

复习的时候发现这个dfs做法也行 就是两种选择 选或不选当前数

#include <bits/stdc++.h>

using namespace std;

const int N = 30;
int n, k, cnt;
int a[N];

bool is_prime(int x)
{
    if (x < 2) return false;
    for (int i = 2; i <= x / i; i ++ )
    {
        if (x % i == 0) return false;
    }
    return true;
}

void dfs(int m, int sum, int startx) //m表示当前已经选择了几个数 sum表示当前选择数的和 startx表示当前选择数的序号 保证是升序序列 避免重复 
{
    if (startx > n) return; //搜出边界了直接返回
    if (m == k)
    {
        if (is_prime(sum)) cnt ++ ;
        return;
    }

    //两种选择
    dfs(m, sum, startx + 1);
    dfs(m + 1, sum + a[startx], startx + 1); 

    return;
}

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    dfs(0, 0, 0);

    printf("%d\n", cnt);

    return 0;
}

我的错误做法 只能看数组内连续的几个数是否是质数
比如选3个数就不能看第1个第3个第4个数加起来的情况

#include <bits/stdc++.h>

using namespace std;

const int N = 30;
int n, m, cnt, res;
int arr[N];

bool is_prime(int x)
{
    if (x < 2) return false;
    for (int i = 2; i <= x / i; i ++ )
    {
        if (x % i == 0) return false;
    }
    return true;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &arr[i]);

    for (int i = 1; i <= n; i ++ )
    {
        res = 0;
        for (int j = i; j - i <= m -1 && j <= n; j ++ )
        {

            res += arr[j];
            //cout << res << endl;
            if (j - i == m - 1)
                if (is_prime(res)) cnt ++ ;
        }

    }

    printf("%d\n", cnt);

    return 0;
}
posted @ 2024-04-16 16:49  MsEEi  阅读(26)  评论(0)    收藏  举报