从数列到贪心:算法竞赛基础题的三类常见坑解析
这几天整理手头的刷题笔记看到有一套小题目让我印象很深题名干脆利落数列1、修理牛棚、按要求输出序列。乍看都是入门难度但把它们放在一起研究会发现问题往往不只在“会不会算法”更多是出在读题、建模和输出这三件事上。这套题特别适合刚接触算法竞赛、正在刷基础题的读者也适合很久没碰代码、想把信息学手感捡回来的朋友。下面我就按这三个题目展开把思路推导、代码实现和现场容易踩的坑一次性说清楚。1. 数列1这台送分题为什么总有人卡在“输出”上1.1 先把题干翻译成可执行的序列规则“数列1”这类题目的套路可以说是入门第一课题目给你一个序列的定义方式可能给前几项也可能给递推式或通项公式然后让你输出前 n 项或第 n 项。难倒大多数人的不是找规律而是把题干的文字描述准确翻译成循环代码。做这类题我建议先做三件事。第一确认题目要输出的是“前 n 项”还是“第 n 项”前者通常用循环输出后者可以只算一个变量。第二确认数列下标从 1 开始还是从 0 开始很多人在这种地方犯低级错误。第三确认数值范围n 一旦到几十万斐波那契类的结果很快超过 int必须用 long long 甚至取模。举个例子假设题目要求输出前 n 项通项公式是 a_i i^2 - i 1。这个规则很简单但代码里最容易出错的是乘法溢出。如果直接写i * i在 n 较大时 int 会先溢出再赋值结果就错了。常见的写法是先把 i 转成 long long或者用1LL * i * i强制提升类型。1.2 演示一份可以直接跑的数列生成代码对于“数列1”这种题我的实现习惯是单函数解决尽量不加多余逻辑#include bits/stdc.h using namespace std; int main() { int n; cin n; for (int i 1; i n; i) { long long val 1LL * i * i - i 1; cout val (i n ? \n : ); } return 0; }代码本身没什么好讲的关键点在于(i n ? \n : )。这个 ternary 表达式保证了行尾没有多余空格也保证了最后一定有换行。很多测评系统对行尾空格并不敏感但有些严格比对所以写成这种格式永远是最保险的。再扩展一点如果题目给的是递推式比如 a_1 1a_2 1a_i a_{i-1} a_{i-2}那处理方式也类似只是需要一个数组或两个滚动变量。数组写起来更直观滚动变量更省内存对于普通入门题数组完全够用。真正重要的是把公式写在循环里并且保证边界项 a_1、a_2 能正确初始化。1.3 为什么“数列1”这种题还值得写一篇复盘有人会觉得这种题很简单不值得花时间。但实战中我见过不少人在这种题上翻车主要翻在三个地方一是没看清输出项数少输出一项或多输出一项二是数据类型没用 long long大样例直接爆掉三是换行和空格处理得随随便便结果本地看起来没问题提交就是 WA。所以“数列1”这种题真正考察的是读题的严谨性和输出习惯。入门阶段把这两件事练好后面做复杂题会省很多排查时间。你可以把这类题当成一种“格式化输出基本功”它的价值不在算法而在让代码风格从一开始就规范起来。2. 按要求输出序列先把输出规则从题干里完整抄出来2.1 输出规则比算法更值得逐字读“按要求输出序列”这个标题听上去很泛实际指一类特别常见的任务输入一个 n按某种规则输出一个序列或者输出一个二维结构比如三角形、矩阵、螺旋线。题目本身可能很简单但输出格式一旦搞错算法再对也是零分。我后来总结了一条经验拿到这种题不要在脑子里直接开始写主逻辑先把输出规则“抄”到草稿纸上。具体来说要看清楚以下这些细节数字之间用空格还是逗号分隔每行末尾允不允许多余空格输出完毕之后是否需要换行如果有多个测试用例每个用例之间是否有空行输出英文、数字还是特殊符号是否要保留小数保留几位十六进制、八进制还是十进制是否需要补前导零。这些细节里最容易被轻视的就是“行尾空格”和“最后换行”。判断标准很简单测评系统一般用文件比较有的忽略行尾空白有的逐字符比较。你没法预判所以干脆按最严格的标准来写行末不输出多余空格整个输出最后一个字符后跟上换行符。2.2 用“first”标记统一处理分隔符处理行内序列分隔符我首选的模式不是判断i n而是用一个 bool 变量记录是否已经输出过第一个元素bool first true; for (int i 1; i n; i) { if (!first) cout ; first false; cout val; } cout \n;这种写法比i n更通用尤其当循环不是从 1 到 n 的时候。比如你根据某个条件在循环里筛选输出筛选出的个数不确定用first标记就不会出现多一个空格或少一个空格的问题。同理如果题目要求输出用逗号分隔只需要把cout 改成cout ,逻辑完全不变。在 Python 里更省事直接print( .join(map(str, arr)))就保证不会有多余空格。但需要注意join 要求列表元素都是字符串所以要先 map 成 str。用 C 时没有这么方便的函数所以first标记是最稳定的写法。2.3 二维输出的换行和空行陷阱除了普通序列很多题要求按矩阵格式输出。这里常见的问题是“空行位置”。比如两个矩阵测试样例之间要有一个空行而最后一个样例后面不能跟空行。很多初学者会在每个样例结束后都输出空行结果末尾多了一个空行被判 Presentation Error 或 Wrong Answer。处理方式通常是给每个测试用例的结果用一个变量控制判断当前是不是第一个用例只有非第一个用例才在输出前先补一个空行。这样最后一个用例结束后不会多出空行。这种“前缀空行”的思路比“后缀空行”要稳妥得多。下面这个表是我现在做输出题之前必查的清单建议你也抄一份检查项常见错误推荐做法数字分隔符句末多空格用 first 标记或条件判断行尾换行最后一组没有换行统一在输出后追加\n多组样例空行末尾多空行用“前缀空行”模式数据类型大数溢出先算乘法再看范围格式类型该用十六进制用了十进制逐字抄下题目要求大小写Hex 输出成 hex复制样例别手打3. 修理牛棚从零推演贪心断板法3.1 这个经典问题到底在求什么“修理牛棚”是信息学里非常有名的贪心题你会在很多公开题目和练习册里见到它的变体。题目大意是有一排牛棚门被暴风雨打坏了棚里有牛的 C 个位置编号可能不连续。现在最多能用 M 块木板去修门每块木板可以覆盖任意长度的一段连续牛棚。要求用这些木板覆盖住所有有牛的牛棚同时让木板总长度最小求这个最小总长度。这个问题的第一反应很容易是数位 DP 或动态规划因为 M 和位置都有限制DP 也能做。但贪心解法更简短也更体现思维含量。核心是把“增加一块木板”理解成“在原有连续覆盖范围里切开一个空隙”。如果只有一块木板那必须从最左边有牛的牛棚一直盖到最右边有牛的牛棚总长度就是最后一个位置 - 第一个位置 1。比如位置是 2、8、15、30、42、50一块木板要覆盖 2 到 50长度是 49。这个长度当然很大因为中间很多牛棚没有牛也被一并盖上了。如果有两块木板就可以在某一处“断开”。断开的位置应该选在没有牛的连续空隙里。每断开一个空隙被木板覆盖到的牛棚总数就会减少一个空隙长度。所以想让木板总长度最小就是要选出一些尽可能大的空隙不被木板覆盖。3.2 贪心策略为什么是选最大空隙继续把问题抽象原本是一整段连续覆盖现在最多增加 M 块木板意味着最多可以有 M-1 个断点也就最多可以选择 M-1 个空隙“省掉”。省掉的空隙越多木板总长度越小。因此策略很简单给所有有牛牛棚的位置排序计算相邻有牛位置之间的空隙长度把空隙按长度从大到小排序优先选择最长的那 M-1 个空隙从总跨度中减去。这里会有一个疑问为什么相邻有牛位置之间的空隙长度是pos[i] - pos[i-1] - 1因为如果两个有牛位置分别是 15 和 30那么中间没有牛的牛棚是 16 到 29一共 30 - 15 - 1 14 个。这 14 个位置如果能断开不盖木板总长就减少 14。贪心正确性的逻辑也很直白每一次增加一块木板都是选择当前最大的空隙来省掉。前面选择的大空隙不会影响后续选择其它空隙的收益所以每一步的局部最优组合起来就是全局最优。这就是典型的“排序取最大”型贪心。我用一组位置数据来手算一遍位置 2、8、15、30、42、50假设 M 3。首先排好序总跨度 50 - 2 1 49。相邻空隙分别是2 和 8 之间58 和 15 之间615 和 30 之间1430 和 42 之间1142 和 50 之间7从大到小排序是 14、11、7、6、5。因为最多用 3 块木板最多选择 2 个空隙选 14 和 11得到答案 49 - 14 - 11 24。用三块木板实际分配一下木板 1 盖 2 到 15长度 14木板 2 只盖 30 这一个牛棚长度 1木板 3 盖 42 到 50长度 9。三块加起来正好 24同时所有有牛位置都被覆盖到了。这个结果非常符合直觉最长的两个空隙被省掉了木板总长度自然最小。3.3 修理牛棚的完整参考代码#include bits/stdc.h using namespace std; int main() { int M, S, C; cin M S C; vectorint pos(C); for (int i 0; i C; i) { cin pos[i]; } sort(pos.begin(), pos.end()); int ans pos.back() - pos.front() 1; vectorint gaps; for (int i 1; i C; i) { int gap pos[i] - pos[i - 1] - 1; if (gap 0) gaps.push_back(gap); } sort(gaps.rbegin(), gaps.rend()); for (int i 0; i M - 1 i (int)gaps.size(); i) { ans - gaps[i]; } cout ans \n; return 0; }代码里有三处细节值得说。一是sort(pos.begin(), pos.end())题目输入可能不按顺序必须排序。二是gap 0才入数组因为相邻位置相等这种情况虽然实际输入里很少见但加了判断代码更稳。三是循环条件里i M - 1 i (int)gaps.size()这保证即使 M 很大、空隙不够多也不会越界。还有一个很容易被忽略的情况当 M C 时每头牛用一个单独的长度 1 木板就好总长度就是 C。上面的代码会自动处理这种情况吗会。因为这时能减去的空隙数量受gaps.size()限制最多把 C-1 个空隙全部减掉最后ans刚好等于 C。输入里 C 可能等于 1此时gaps为空ans就是 1也没有任何问题。4. 实测中真正容易翻车的边界三个问题一起复盘4.1 边界条件和数据范围优先单独测试把这三道题放在一起刷最大的收获是“边界条件几乎决定成败”。很多选手样例过了就直接交结果正式数据一来就 WA原因往往不是主算法错了而是某个边界情况没处理。数列题要注意 n 等于 1 或 n 等于 0 的情况。n 等于 1 时循环只跑一次输出一个数就好但别忘了换行。n 等于 0 时严格来说不应该有输出有些题目保证 n 0但如果不保证就应该加一句判断直接返回。修理牛棚要注意 M 非常大和 C 非常小的情况。比如 M 50C 1这时候只需要一块长度为 1 的木板。如果代码里对pos.back()和pos.front()做运算C 1 时这两个值相同ans 1后面的循环也不会执行结果正确。再比如 C 3位置是 1、2、3M 2。这时总跨度是 3唯一空隙是位置 2 和 3 之间其实位置是连续的空隙长度为 0。排序后没有实际空隙可以选择最终 ans 还是 3因为两块木板没有必要分开直接一块长度 3 就能解决问题。4.2 走查法比反复提交更高效我现在调试这类基础题的习惯是“走查法”写完代码不急着提交先去把题目给出的样例手工模拟一遍再设计几个极端样例。所谓走查就是拿着笔和样例数据一行一行地按代码逻辑在纸上执行看输出是否和预期一致。比如修理牛棚这道题我会主动构造这几个测试输入中牛棚位置没有排序比如 50、2、30检查排序是否生效M 非常大比如 M 100C 5检查答案是否等于 C所有牛都集中在连续位置比如 1、2、3答案是否不受木板数量影响答案会很大检查 int 和 long long 是否够用。构造完极端用例再跑一遍基本能排除 90% 的边界问题。这个方法看着笨但比起反复提交试错效率高得多也更能培养对代码的掌控感。4.3 输出问题要当成独立知识点练习前面说的输出格式看起来是小问题但实际考试里影响很大。我的习惯是准备一个小模板嵌套到所有题目里去for (int i 0; i n; i) { if (i ! 0) cout ; cout arr[i]; } cout \n;这个模板没有用i n - 1来做判断而是用if (i ! 0)哪怕循环内逻辑复杂它也能保证中间有空格、末尾不空格。写矩阵输出时我再套一层“前缀空行”的处理逻辑多组样例之间就不会出现多余空行。把这些输出模式练熟基本可以把输出这块的丢分降到零。5. 顺手复盘三道题暴露的基础能力模型5.1 读题能力、建模能力和输出能力是三条腿把“数列1”、“修理牛棚”、“按要求输出序列”放在一起你会发现它们恰好对应三种基础能力读题、建模、输出。数列题考察能不能把文字规则变成循环修理牛棚考察能不能把真实场景抽象成排序和空隙输出序列题考察能不能把计算结果精确地格式化。这三种能力在复杂题目里会同时出现。与其等到做难题时手忙脚乱不如在入门阶段就把每一条腿都练稳。这也是我对这套小题目评价比较高的原因它没有堆砌复杂算法却逼着你在每一个环节都做到规范。5.2 刷基础题的正确姿势和心态我见过不少学习者刷题只看题解把答案抄一遍就感觉自己会了。但基础题的核心价值不是“知道解法”而是“亲手完成从题意到代码的闭环”。建议每道题做完之后哪怕 AC 了也停留五分钟想三个问题如果数据范围扩大十倍代码还能跑吗如果输出格式改成逗号分隔需要改哪里题目描述换一种场景本题的模型还能套用吗5.3 个人向的小经验我在实际写题过程中感受最深的一点是基础题一定要慢。这不是说做题速度慢而是说不要在思路还没成型时就开始敲键盘。尤其像修理牛棚这类题先把样例数据和公式手算一遍代码写起来就顺很多。还有一个小经验是别急着把bits/stdc.h当成万能药水。在本地用没问题但还是要清楚头文件里包含的东西以及为什么vector能用sort和rbegin()。基础阶段的每一个细节都是在给后面更复杂的题打地基。

相关新闻

微服务全链路瓶颈定位平台选型对比与落地实践指南

微服务全链路瓶颈定位平台选型对比与落地实践指南

微服务一多起来,链路排查这事儿就从“偶尔头疼”变成了“天天头疼”。你盯着监控面板,CPU没爆、内存没满、接口的TP99却一路飙红,到底是哪个服务拖了后腿、哪次调用把线程池堵死了,全靠肉眼在一堆日志里捞。这不是个例&#xff0c…

2026/10/13 8:06:30 阅读更多 →
黑盒测试决策表设计:多条件组合用例的完整实战指南

黑盒测试决策表设计:多条件组合用例的完整实战指南

1. 为什么黑盒测试需要决策表做黑盒测试这么多年,决策表是我手里最趁手的工具之一。你要处理的不只是单个输入正确与否,而是多个条件互相咬合时,系统到底该走哪条分支。等价类划分能解决单条件的不同取值,边界值分析能揪出临界点的…

2026/10/13 8:06:30 阅读更多 →
Java大厂面试复盘:Stream、Spring Boot、Redis、微服务考点与路径

Java大厂面试复盘:Stream、Spring Boot、Redis、微服务考点与路径

互联网大厂Java面试,这几年越来越不像“背题大会”了。很多朋友让我复盘带人模拟面试的过程,刚好前几天陪一位叫谢飞机的同学做了一轮完整的实战演练,方向就压在Java、Stream、Spring Boot、Redis、微服务这四个块上。他基础不算差&#xff0…

2026/10/13 8:06:30 阅读更多 →

最新新闻

跨境电商独立站运营实战:从选品到流量获取

跨境电商独立站运营实战:从选品到流量获取

1. 项目背景与市场机会跨境电商独立站近年来成为许多创业者的首选赛道,相比平台电商模式,独立站具有更高的利润空间和品牌溢价能力。根据行业数据显示,成功运营的独立站平均毛利率可达50-70%,远高于亚马逊等第三方平台的15-25%。这…

2026/10/13 8:59:49 阅读更多 →
分布式探针Agent-Reach:端到端网络可达性监控的架构与实践

分布式探针Agent-Reach:端到端网络可达性监控的架构与实践

1. 项目概述1.1 核心需求解析做运维或者后端开发的朋友一定不陌生,线上系统出问题十次有八次是从"某个服务连不上"开始的。尤其这套架构一旦铺到微服务、多云多机房、边缘节点这种分布式环境里,节点之间互相依赖、调用链路盘根错节&#xff0c…

2026/10/13 8:59:49 阅读更多 →
高可靠性继电器选型指南:从厂家资质到寿命验证的完整要点

高可靠性继电器选型指南:从厂家资质到寿命验证的完整要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 8:59:49 阅读更多 →
计算机网络概述:不是入门课,而是数据交付的元逻辑

计算机网络概述:不是入门课,而是数据交付的元逻辑

1. 为什么“计算机网络01——概述”不是入门课,而是整座知识大厦的地基?你有没有试过:学完“OSI七层模型”,能默写出每一层名字和典型协议,但一看到Wireshark里密密麻麻的TCP重传包就发懵;背熟了“子网掩码…

2026/10/13 8:59:49 阅读更多 →
IP地址库离线解析实战:从2019快照到批量归属查询

IP地址库离线解析实战:从2019快照到批量归属查询

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 8:59:49 阅读更多 →
Python Tkinter GUI开发全指南:从窗口搭建到打包发布

Python Tkinter GUI开发全指南:从窗口搭建到打包发布

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 8:58:45 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →