【洛谷P1036】 [NOIP2002 普及组] 选数

一、题目:

二、解题思路:

本文章采用的解决方法是递归DFS(深度优先搜索)。

以下图是思路图:

1.首先-确定位置

题目说4个数字取三个数,所以考虑的只有三个位置和这三个位置分别放什么数值。从第一个位置开始放数。

2.其次-开始放数

分为4种可能,第一位置可以先放3,那么第二个位置就只能放7,12,19这三个数字任意一个数字了,假设放了7,那么第三个位置就只能放12或19。所以本次得到的结果是3 7 12或3 7 19.(这里只谈的简单思路,编写代码的或者写类似搜索题要注意回溯问题和恢复现场)。接着再继续列举其他可能性,得到最后结果为3 7 12,3 7 19,3 12 19,7 12 19这四种结果,发现与题目给的样例一致。

3.最后-考虑素数如何判断

素数是指只能被1和它自身整除的大于1的自然数。因此,判断一个数是否为素数,需要满足以下几个条件:

  1. 大于1的自然数:素数必须大于1,因为1不是素数。同时,它必须是自然数,即非负整数。

  2. 只有1和自身两个正因数:这是素数最核心的定义。也就是说,除了1和它本身以外,没有其他正整数能够整除它。如果一个数有除了1和它本身以外的其他正因数,那么它就不是素数。

素数(质数)的判断:检查该数是否能被比它小的任何正整数整除。如果能被整除,则它不是素数;如果不能被任何比它小的正整数整除,则它是素数。

步骤如下:

  • 排除小于等于1的数:任何小于等于1的数都不是素数。
  • 排除偶数:除了2以外,所有的偶数都不是素数,因为它们都可以被2整除。
  • 检查奇数因子:从3开始,到被检测数的平方根为止,检查是否能被这些奇数整除。如果能被整除,则不是素数;如果不能被整除,则继续检查下一个奇数。
  • 达到平方根即可停止:如果一个数有一个大于它平方根的因子,那么它必然还有一个小于或等于其平方根的因子。因此,我们只需要检查到被检测数的平方根即可。

三、分步骤-实现代码

1.先设置好全局变量

#include<iostream>
using namespace std;

const int N = 30;
int arr[N];//存答案
int q[N];//存数据
int n,k;
int res;//保存题目要求的种类数

2.主函数

1)for循环用来给数组赋值

2)函数传参-当前枚举到哪个位置,从第一个位置开始

int main()
{
    scanf("%d %d",&n,&k);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&q[i]);
    }
    
    dfs(1,1);
    
    printf("%d\n",res);
    
    return 0;
}

3.开始遍历

利用for循环遍历

void dfs(int x, int start)//x表示当前到了哪个位置,start表示从几开始枚举
{
    //选谁
    for(int i = start; i <= n; i++){
        arr[x] = q[i];
        dfs(x + 1 , i + 1);//深度优先 继续向下
        arr[x] = 0;//恢复现场
    }
}

 遍历完所有数字以后开始求出总和,并进行判断是否为素数

注意:

  • dfs函数的最后,当x > k时,有一个return;语句。这个return;是用来结束当前递归调用的,确保函数返回。虽然函数的定义是void dfs,即没有返回值,但是return;语句是必需的,当条件x > k满足时,我们不再需要继续递归搜索,而是应该返回到上一层递归调用。
if(x > k)
    {
        int sum = 0;
        for(int i =1; i <= k; i++){
            sum = sum + arr[i];
        }
        
        //prime number意思是素数
        if(is_prime(sum)){
            res++;
        }
        return ;
    }
    

4.素数判断

bool is_prime(int sum){
    if(sum < 2) return false;
    for(int i = 2; i <= sum / i; i++){//如果写成i*i<=sum,数据大的时,可能会爆出int的最大范围,此处还可以写成i<=sqrt(sum)
        if(sum%i==0)
        {
            return false;
        }
    }
    return true;//此处有好几处判断以后才能返回true,易错点在if判断完以后用else return true.这样写就错了
}

四、最终代码

#include<iostream>
using namespace std;

const int N = 30;
int arr[N];//存答案
int q[N];//存数据
int n,k;
int res;//保存题目要求的种类数

bool is_prime(int sum){
    if(sum < 2) return false;
    for(int i = 2; i <= sum / i; i++){//如果写成i*i<=sum,数据大的时,可能会爆出int的最大范围,此处还可以写成i<=sqrt(sum)
        if(sum%i==0)
        {
            return false;
        }
    }
    return true;//此处有好几处判断以后才能返回true,易错点在if判断完以后用else return true.这样写就错了
}

void dfs(int x, int start)//x表示当前到了哪个位置,start表示从几开始枚举
{
    if(x > k)
    {
        int sum = 0;
        for(int i =1; i <= k; i++){
            sum = sum + arr[i];
        }
        
        //prime number意思是素数
        if(is_prime(sum)){
            res++;
        }
        return ;
    }
    
    //选谁
    for(int i = start; i <= n; i++){
        arr[x] = q[i];
        dfs(x + 1 , i + 1);//深度优先 继续向下
        arr[x] = 0;//恢复现场
    }
}

int main()
{
    scanf("%d %d",&n,&k);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&q[i]);
    }
    
    dfs(1,1);
    
    printf("%d\n",res);
    
    return 0;
}

如果这篇文章有任何问题或建议请评论留言,看到了会及时解决

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值