P1036 [NOIP 2002 普及组] 选数

P1036 [NOIP2002 普及组] 选数 在dfs函数中,我们明显能看到一个参数 startx。比如说在6里面随便选5个数,那么选法都是什么呢?其实这里的难点是:def算法 加上 如何去重?是startx而不是start是为了避免关键字。个整数相加,可分别得到一系列的和。知道了不降原则,咱们再来see see 代码。现在,要求你计算出和为素数共有多少种。”应该是4,因为是升序排列。也就是最小的,符合不降原则的参数。就是这样的,枚举还是蛮清晰的吧。所以说,这样就可以避免判重了。咱们最主要看其中不降原则的部分。输出一个整数,表示种类数。 阅读详情

记录140

#include<bits/stdc++.h> 
using namespace std; 
const int N=25; // 定义常量N,表示n的最大范围(题目n<=20)
int n,k; // n表示整数个数,k表示需要选出的个数
int a[N]; // 记录n个整数
int ans=0; // 记录和为素数的组合种类数
// 判断一个数x是否为素数
bool is_prime(int x){
    if(x<2) return false; // 小于2的数不是素数
    for(int i=2;i*i<=x;i++){
        if(x%i==0) return false; // 如果能被i整除,不是素数
    }
    return true; // 是素数
}

// 深度优先搜索(DFS)
// start: 当前从第几个数开始选(保证组合不重复且有序)
// cur: 当前已经选了几个数
// sum: 当前已选数的总和
void dfs(int start,int cur,int sum){
    if(cur==k){ // 如果已经选够了k个数(递归终止条件)
        if(is_prime(sum)) ans++; // 判断总和是否为素数,种类数加1
        return; // 结束当前递归分支
    }    
    // 可行性剪枝:剩下的数必须足够凑齐k个
    // 当前从start开始选,还需要选(k-cur)个数,所以i的上限是 n-(k-cur)+1
    // 循环终点 = 总数 - 还需要选的个数 + 1
    for(int i=start;i<=n-(k-cur)+1;i++){
        dfs(i+1,cur+1,sum+a[i]); // 从下一个位置开始选,已选数量+1,总和加上a[i]
    }
}
int main(){ // 主函数入口
    ios::sync_with_stdio(false); // 关闭标准流同步,提升输入输出效率
    cin.tie(0); // 解除cin与cout的绑定,加快读取速度
    cin>>n>>k; // 读入n和k
    for(int i=1;i<=n;i++) cin>>a[i];// 读入n个整数 
    dfs(1,0,0); // 从第1个数开始选,当前选了0个,当前总和为0
    cout<<ans; // 输出和为素数的组合种类数
    return 0; // 程序正常结束
}

题目传送门https://www.luogu.com.cn/problem/P1036


前言

我是一名专注信奥赛(CSP-J/S、NOIP)的教练。

  • 如果你觉得这篇题解对你有帮助,欢迎点击关注我的优快云账号,我会持续更新高质量算法解析。
  • 我深知算法思维的构建远比单纯通过题目更重要,本系列题解不局限于AC代码的堆砌,而是致力于拆解题目背后的逻辑链条与核心知识点
  • 备赛路上若遇瓶颈,欢迎随时评论或私信,我将甄选典型疑难问题,通过视频讲解或撰写专项文章的形式,为你提供深度答疑。

核心解题思路

这道题是一道非常经典的组合枚举与素数判定问题。

  1. 问题转化:
    题目要求从 n个整数中任选 kk 个相加,统计和为素数的组合种类数。这本质上是一个“从 n 个数中取 k 个数的组合问题”(即 Cnk ),而不是排列问题。因为加法满足交换律,3+7+19 和 7+3+19 是同一个组合,我们需要避免重复计算。

  2. 算法选择(DFS + 剪枝):
    由于 n≤20 ,数据规模较小,我们可以使用深度优先搜索(DFS)来暴力枚举所有可能的组合。

    • 避免重复:在 DFS 过程中,我们通过一个 start 参数来记录当前从哪个位置开始选数。每次递归选择下一个数时,都从当前位置的下一个位置(i+1)开始选,从而保证选出的数在原数组中的下标是严格递增的,天然避免了组合的重复。
    • 可行性剪枝:在枚举当前层的选择时,如果剩下的数字个数不足以凑齐 kk 个数,就提前停止枚举,减少无效搜索。
    • 素数判定:当凑齐 kk 个数时,计算它们的和,并使用试除法判断该和是否为素数。

 代码分块详细解释

1. 头文件、常量与全局变量定义

#include<bits/stdc++.h> 
using namespace std; 
const int N=25; // 定义常量 N,表示 n 的最大范围(题目 n<=20)
int n,k; // n 表示整数个数,k 表示需要选出的个数
int a[N]; // 记录 n 个整数
int ans=0; // 记录和为素数的组合种类数
  • 详细分析:这部分定义了算法的基础变量。a[N] 用来存储输入的 nn 个整数,ans 作为全局计数器,用来累加满足条件的组合数。

2. 素数判定函数(is_prime)

// 判断一个数 x 是否为素数
bool is_prime(int x){
    if(x<2) return false; // 小于 2 的数不是素数
    for(int i=2;i*i<=x;i++){
        if(x%i==0) return false; // 如果能被 i 整除,不是素数
    }
    return true; // 是素数
}
  • 详细分析:这是一个标准的试除法求素数函数。
    • 边界处理:首先排除了小于 2 的情况,因为 0 和 1 不是素数。
    • 循环优化:循环的终止条件是 i*i <= x(即 i≤xi≤x​ )。这是因为如果 xx 有一个大于 xx​ 的因子,那么它必然有一个小于 xx​ 的对应因子。这种优化将素数判定的时间复杂度从 O(x)O(x) 降低到了 O(x)O(x​) ,对于本题中可能达到 5×106×205×106×20 级别的和来说,效率提升非常明显。

3. 核心逻辑:DFS 组合枚举与剪枝

// 深度优先搜索(DFS)
// start: 当前从第几个数开始选(保证组合不重复且有序)
// cur: 当前已经选了几个数
// sum: 当前已选数的总和
void dfs(int start, int cur, int sum){
    if(cur==k){ // 如果已经选够了 k 个数(递归终止条件)
        if(is_prime(sum)) ans++; // 判断总和是否为素数,种类数加 1
        return; // 结束当前递归分支
    }    
    // 可行性剪枝:剩下的数必须足够凑齐 k 个
    // 当前从 start 开始选,还需要选 (k-cur) 个数,所以 i 的上限是 n-(k-cur)+1
    // 循环终点 = 总数 - 还需要选的个数 + 1
    for(int i=start; i<=n-(k-cur)+1; i++){
        dfs(i+1, cur+1, sum+a[i]); // 从下一个位置开始选,已选数量+1,总和加上 a[i]
    }
}
  • 详细分析:这是代码的灵魂,完美实现了组合枚举。
    • 递归终止条件:当 cur == k 时,说明已经凑齐了 kk 个数,此时直接调用 is_prime(sum) 进行判定,如果为真则全局答案 ans 加 1。
    • 避免重复的核心:for 循环的起始值是 start,而递归调用时传入的是 i+1。这意味着如果当前选择了第 i 个数,下一层递归只能从 i+1 及之后的数中选择。这保证了选出的数在原数组中的下标是严格递增的,从而避免了 3+7+19 和 7+3+19 这种重复计算。
    • 可行性剪枝:循环的终止条件 i <= n-(k-cur)+1 是一个经典的组合数学优化。它的意思是:当前还需要选 k-cur 个数,如果从当前位置 i 往后数,剩下的数字个数已经不够填满这 k-cur 个位置了,那么继续枚举 i 及其后面的数就没有意义了,直接跳出循环。

4. 主函数:输入处理与启动搜索

int main(){ 
    ios::sync_with_stdio(false); 
    cin.tie(0); 
    cin>>n>>k; // 读入 n 和 k
    for(int i=1;i<=n;i++) cin>>a[i]; // 读入 n 个整数 
    dfs(1,0,0); // 从第 1 个数开始选,当前选了 0 个,当前总和为 0
    cout<<ans; // 输出和为素数的组合种类数
    return 0; 
}
  • 详细分析:主函数负责数据的读入和算法的启动。dfs(1,0,0) 表示从数组的第 1 个位置开始尝试选择,当前已经选了 0 个数,当前的累加和为 0。

核心逻辑总结表

代码模块核心变量/操作精炼作用解决的痛点
素数判定is_prime(int x)试除法判断一个数是否为素数准确判定组合的和是否符合题目要求,i*i<=x 保证了效率
递归终止if(cur==k)判断是否已经选够了 k 个数到达搜索树的叶子节点,进行结果统计
避免重复dfs(i+1, ...)下一层递归只能从当前数的下一个位置开始选保证选出的组合下标严格递增,避免了排列带来的重复计算
可行性剪枝i <= n-(k-cur)+1限制当前层枚举的上界当剩余数字不足以凑齐 k 个数时提前终止,大幅减少无效搜索
状态传递sum+a[i]将当前选择的数累加到总和中在递归过程中动态维护当前组合的总和,避免了在叶子节点重新遍历求和
全局计数ans++累加满足条件的组合数记录最终的答案
【洛谷题解】P1036 [NOIP2002 普及组] 选数 难度相较于之前有一些提升,需要使用DFS算法和判断素数才可以解出来这道题。 阅读详情

相关推荐

Halcon实战:5种边缘检测算子对比(Sobel、Roberts、Prewitt、Kirsch、Canny)

本文深入对比了Halcon中Sobel、Roberts、Prewitt、Kirsch和Canny五大边缘检测算子的实战表现与核心差异。通过工业图像实例与代码解析,提供了基于噪声水平、边缘连续性及实时性要求的场景化选型指南,帮助工程师精准选择最适合的算子以优化视觉检测流程。

weixin_29266007的博客 393

洛谷P1036 [NOIP 2002 普及组] 选数

已知n个整数x1​x2​⋯xn​,以及1个整数kkn从n个整数中任选k个整数相加,可分别得到一系列的和。例如当n4k34个整数分别为37121937122237192971219383121934现在,要求你计算出和为素数共有多少种。371929。

tonman7797的博客 952

华南理工大学电子与信息学院23年预推免复试面试经验贴

华南理工大学电子与信息学院23年预推免复试面试经验贴

Scut_Ty的博客 3299

P1036 [NOIP2002 普及组] 选数 题解

P1036 [NOIP2002 普及组] 选数 题解

yaosichengalpha的博客 987

APP抓包-代理转发绕过反代理+Xposed绕过证书校验

而这里的牛牛不止设置了反代理还设置了证书校验,一闪而过的三个数据包就是在校验证书。而proxifier工具的转发机制获取的是网络接口出口数据,相当于是在网络接口设代理。这个时候就可以在夜神打开牛牛开始抓包了,打开牛牛可以看到proxifier有夜神流量通过。关闭代理之后牛牛就正常了,可恶的牛牛啊,这样我没办法抓包啊,怎么办啊。模拟器开启代理,burp开启监听。打开牛牛app,牛牛不在报错。牛牛app检测的是系统代理,也就是在模拟器或手机上设置的代理。再在浏览器下载两个模块,在夜神安装,用来屏蔽证书校验。

xiaoheizi的博客 7532

基于深度残差U-Net与多尺度注意力机制的医学图像分割系统

本文提出了一种创新的医学图像分割系统,通过融合残差连接、通道注意力机制和空间金字塔池化技术,构建了高性能的UResnet分割网络。系统采用编码器-解码器架构,创新性地引入多层次特征增强模块,显著提升了医学图像中复杂组织结构的分割精度。实验结果表明,该方案在多个医学图像数据集上均取得了优异性能,边界分割精度提升15%,训练收敛速度提升30%,同时减少20%计算量。系统为临床诊断提供了可靠的技术支持,未来可扩展至3D医学图像分割和联邦学习等方向。

Henry的博客 814

【题解】【洛谷P1036】【子集枚举】—— [NOIP2002 普及组] 选数

平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。枚举算法题中有这样一类题目,给你一些元素,让你从中选出几个,也就是。:是指全集去除了某个集合后剩下的元素组成的集合,例如。:指两个集合中同时存在的元素组成的集合,可得。在集合中,是包含的一种特殊情况,要判断元素。

Pantheraonca的博客 2612

Git救援:如何从误操作中恢复未提交的更改

摘要:本文探讨了误操作git reset --hard导致未提交更改丢失后的恢复方法。首先分析git reflog的局限性,随后介绍利用IDE本地历史记录(如IntelliJ IDEA和VS Code)进行文件恢复的具体步骤。最后建议使用文件系统恢复工具(如Linux的extundelete或Windows的Recuva)作为备选方案。文章强调定期提交的重要性,同时提供了意外发生时可行的恢复策略,帮助开发者尽可能挽回丢失的工作内容。(149字)

2501_90323865的博客 129

洛谷P1036 [NOIP2002 普及组] 选数

洛谷P1036 [NOIP2002 普及组] 选数 题解

PengFly123的博客 398

uniapp [安卓苹果App端] - 实现微信支付功能+拉起微信付款支付后返回本app页面,详解uniApp如何唤起本机微信app并跳转到支付页面全流程及示例源码(uniapp实现微信支付,可打包)

uni-app项目在APP端使用微信支付功能,uniapp前端实现微信支付,uniapp开发安卓App对接微信支付,uniapp苹果ios唤起微信支付,uniapp调用微信支付uniapp开发app-调用微信支付,uniapp开发安卓苹果app如何接入微信支付,前后端应该怎么做,UniApp中实现微信支付功能,支付报错,调起失败没响应,uniapp苹果微信支付唤起实现,app唤起微信支付,拉起微信支付,uniapp中唤醒微信进行支付并返回app,uniApp调起微信支付失败报错,全网最详细的uniapp微信

🏆 前端领域优质创作者 1万+

Multisim14使用教程详尽版--(2025最新版)

摘要:本文详细介绍了Multisim14电路仿真软件的功能和使用方法。主要内容包括:1)主流电路仿真软件比较,突出Multisim在教学、竞赛设计中的优势;2)软件界面说明,涵盖菜单栏、工具栏、元器件库和仪器仪表;3)18类元器件分类详解及其使用操作;4)15种仪器仪表的原理和应用场景;5)电路原理图设计与仿真流程;6)基本电路分析方法(直流静态工作点分析、交流分析等)。文章还提供了硬件工程师笔试面试相关资源链接,涵盖电阻、电容、三极管等器件知识及AD软件操作指南。通过系统讲解,帮助用户掌握Multisim

XU157303764的博客 2万+

java:P1036 [NOIP2002 普及组] 选数

洛谷题目:P1036 [NOIP2002 普及组] 选数 题目如下: 我的代码: package 算法竞赛; import java.io.*; /* 思路:dfs思想,其中改进下即可,这样就能够得到不重复的组合。 然后就好办了,符合条件的判断sum是否为质数。 */ public class Test1{ static boolean ky = false; static int k = 0; // 接收几个数据 static int ct =

小白成长记 989

SAP 系统间 SSO 的真正价值,从 SM59 到可信 RFC 的安全设计

SAP S/4HANA系统间Single Sign-On(SSO)的核心价值在于集中管理通信身份与信任关系,而非仅减少密码输入。传统固定账号密码方式在系统规模扩大后会面临密码管理、审计追踪等挑战。通过SM59配置可信RFC目标系统,结合SSL证书或SAP Trusted System等强认证机制,能有效降低安全凭据暴露风险。SSO配置需配合授权对象、角色管理等建立清晰的信任边界,实现系统间安全通信的同时保持可审计性。这种方案特别适用于Fiori前端与S/4HANA后端等场景,通过可信RFC传递用户身份,避免

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1063

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

判断素数、回溯法

triticale的博客 1098

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

洛谷:P1036 [NOIP2002 普及组] 选数

dfs+判断质数

qq_74924951的博客 812

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

[ C++ 真题 ] P1036 [NOIP 2002 普及组] 选数

已知n个整数x1​x2​⋯xn​,以及1个整数kkn从n个整数中任选k个整数相加,可分别得到一系列的和。例如当n4k34个整数分别为37121937122237192971219383121934现在,要求你计算出和为素数共有多少种。371929。

Ω 1011

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

c++ 洛谷P1036 [NOIP2002 普及组] 选数

【代码】c++ 洛谷P1036 [NOIP2002 普及组] 选数。

Joe_c651401的博客 412

P1036 [NOIP2002 普及组] 选数 Python题解

已知n个整数x1​x2​⋯xn​,以及1个整数kkn从n个整数中任选k个整数相加,可分别得到一系列的和。例如当n4k34个整数分别为37121937122237192971219383121934现在,要求你计算出和为素数共有多少种。371929。

weixin_52740785的博客 1106

P1036 [NOIP2002 普及组] 选数 Python

P1036 [NOIP2002 普及组] 选数 Python

weixin_43843099的博客 1215
上一篇: P1460 [USACO2.1] 健康的荷斯坦奶牛 Healthy Holsteins
下一篇: P1015 [NOIP 1999 普及组] 回文数
谢天谢地AC了 | 信奥教练
博客等级 码龄1年 53粉丝 177原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值