洛谷P1036选数

题目描述

已知 n 个整数 1,2,⋯ ,x1​,x2​,⋯,xn​,以及 1个整数 k(1<k<n)。从 n 个整数中任选 k 个整数相加,可分别得到一系列的和。例如当 n=4,k=3,4个整数分别为 3,7,12,19 时,可得全部的组合与它们的和为:

3+7+12=22

3+7+19=2

7+12+19=38

3+12+19=34

现在,要求你计算出和为素数共有多少种。

例如上例,只有一种的和为素数:3+7+19=29。

输入格式

第一行两个空格隔开的整数 n,k(1≤n≤20,k<n)。

第二行 n 个整数,分别为 1,2,⋯ ,x1​,x2​,⋯,xn​。

输出格式

输出一个整数,表示种类数。

解题思路

        读题之后其实我们可以想到,这其实是一个组合型枚举问题,即从n个数中取k个数,然后从其中所有结果中去判断是否满足题意,这样的话我们可以使用dfs来解决此类问题,上文说到这是一个组合型枚举问题,如果对组合型枚举不是很熟练的话这边有一个很好的练习题:https://oj.haizeix.com/problem/236

就是递归实现组合型枚举,事实上我们这个题的问题搜索树就是每一次取k个数用结果去验证是否为素数。难点就是如何去枚举到不同的k个数。

        其实这个很简单,k个数就是k层循环嘛,O(∩_∩)O,比如2个数就是双重循环,3个数就是3重循环,但是k个数,k在我们解题之前并不是一个常量,我们设计循环就很难设计┭┮﹏┭┮,既然这样,枚举能解决的问题递归肯定也可以,但是在递归中我们如何去枚举到不同的k个数呢?我们想想其实我们上文中提到枚举解决问题的时候就是一层层迭代,那么第一层从第一个数开始,第二层从第二个数开始,第三层从第三个数......一直到第k层从第k个数字开始。。。

禁止套娃

所以我们递归的话需要的参数其实就三个:

(1)当前枚举到了第几个数(说白了就是现在在第几层循环)

(2) 当前已经枚举了多少个数(因为题目说明了要从n个数中选k个数,所以如果已经枚举了k个数了,那就要回溯了)

(3) 当前位置的和是多少,因为题目要求是求和,那么我们就用一个参数来记录当前位置的和,这样成功枚举了k个数之后可以很快的验证当前的和sum是否符合题目要求(sum是素数)

        判断素数应该不用多说吧,直接从2开始枚举即可,但是中间细节要注意优化,对于数字n判断是否为素数,只需要枚举到根号n即可

bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

就像这样,时间复杂度就是根号n的成程度,大大减少了计算次数~

这样我们就完成了这个题目的解题思路,现在来看代码。

解题代码

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 2e2 + 10;
int nums[MAXN];
int n, k, ans = 0;

//判断是否为素数
bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        //能整除证明n不是素数
        if (n % i == 0) return false;
    }
    //一直没找到证明n的因数只有1和n
    return true;
}

//开始深搜
void dfs(int ind, int num, int sum) {
    //已经搜了k个数,开始回溯
    if (num == k) {
        //判断是否为素数
        if (isPrime(sum)) ans++;
        return ;
    }
    //从当前层数开始递归向下(可以看作进入下一层循环)
    for (int i = ind; i <= n; i++) {
        dfs(i + 1, num + 1, sum + nums[i]);
    }
    return ;
}

int main(){
    cin >> n >> k;
    for (int i = 1; i <= n; i++) scanf("%d", nums + i);
    dfs(1, 0, 0);
    cout << ans << endl;
    return 0;
}

总结

这个题看着好像有点不好入手,其实把它想成k层循环就是一个玩具题目,O(∩_∩)O,事实上就是几步:

(1)从n个数中选取不同的k个数并求和

(2)判断当前sum是否为素数,是的话结果就加一

好了, 这个题题解就说到这,希望对你们有点帮助~

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值