news 2026/9/9 18:11:49

OJ刷题入门:鸡兔同笼问题背后的输入输出与边界条件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OJ刷题入门:鸡兔同笼问题背后的输入输出与边界条件

做OJ刷题的人,十有八九会对鸡兔同笼问题印象很深。我第一次在在线评测系统上做到OJ1004这道题时还觉得挺意外:题目描述就一句话,笼子里关着鸡和兔,数头有n个,数脚有m只,问各几只。这不就是小学奥数题吗?可真正以OJ题目的形式提交代码以后才发现,数学上三秒得出答案,不代表编码一次能通过。本文就借这道“相当于鸡兔同笼问题”的OJ1004,把问题背后的数学模型、输入输出规范、边界条件处理、提交报错排查这些事完整捋一遍。适合刚接触OJ平台、还不熟悉判题规则的新手,也适合准备各类在线笔试的求职者。

1. 内容整体设计与思路拆解

1.1 为什么说它“相当于鸡兔同笼”

很多OJ题不会老老实实把“鸡兔同笼”四个字写在标题里,而是换个外壳,比如停车场里的自行车和汽车、笼子里的小鸡和小猪,但拆开一看全是同一个二元一次方程组。以最原始的鸡兔同笼来说,设鸡有x只,兔子有y只,头的总数是h,脚的总数是f,那么可以列出两个方程:

  • 头的数量:x + y = h
  • 脚的数量:2x + 4y = f

联立解方程组,用第二个式子减去两倍的第一个式子:

2x + 4y - 2(x + y) = f - 2h

左边化简之后就是2y,于是:

y = (f - 2h) / 2

也就是说,兔子的数量等于“多出来的脚数的一半”。换一个更生活的说法:假如笼子里全都是鸡,那脚数应该是2h只,现在实际有f只脚,多出来的每一只脚都说明有一根“兔腿”没有被算对,而每只兔子比鸡多2只脚,所以兔子数就是(f-2h)/2。这个思路比死记公式好记得多,遇到系数变化也更容易套。

但问题是,x和y是真实世界的数量,必须是整数且不能为负数,所以题目真正考察的是:能不能把这些隐藏约束转化成代码里一个不漏的判断条件。大多数提交踩坑都踩在这个地方。

1.2 这道题在OJ题库里的定位

OJ1004这种编号常见于教学型的在线评测系统,从编号就能看出来它属于“入门期必做”的题目。出题人把它安排在前1000多题的区间,目的很明确:让新手用最简单的题目熟悉OJ平台的完整流程——读题、设计输入输出、本地运行、提交代码、查看判题测试报告。很多人在这里就开始踩坑,比如不知道要处理多组输入、不知道输出格式不能多一个空格、不知道类名必须写成Main。

从算法角度来说,这道题没有任何复杂度可言,时间复杂度是O(1)、空间复杂度也是O(1)。但换个角度看,它又是最考验基本功的题,因为它逼着你理解平台规则。会写for循环不意味着你能在OJ平台上通过这道题,这句话我见过太多反例了。

1.3 不同OJ平台的同类题目差异

我最初是在课程OJ上做的,后来又陆续在华为OJ、西科大OJ平台、东华OJ上见过类似的变体。别看题目表面差不多,平台的输入输出要求差异可能很大,这里做一个对比:

平台输入风格输出格式注意事项
华为OJ常见单组输入,用空格分隔一行输出,不能带无关注释隐藏用例较多,特判严格
西科大OJ平台部分题目为多组输入直到EOF每组结果换行需要处理多行输入
东华OJ常见多组数据,以特定标记结束结果之间留空行注意题目里的结束条件
其他通用题库单组和多组都有大小写、下划线敏感一切以题目描述为准

同样的解题逻辑,换一个平台,可能就因为“是否循环读入”而判成WA。这也是我建议每次提交前都重新看一遍输入输出描述的原因,不能看到“相当于鸡兔同笼”就直接把以前某个平台的代码原封不动贴过去。

2. 核心细节解析与实操要点

2.1 判题系统的判定逻辑

OJ判题系统的工作方式,简单来说就是你提交源代码,服务端用事先准备好的测试用例把你的代码编译、运行,然后把程序输出和标准答案逐字符比对。比对的字母缩写大家都应该眼熟:

  • AC(Accepted):恭喜,完全通过
  • WA(Wrong Answer):答案错误
  • TLE(Time Limit Exceeded):超时
  • RE(Runtime Error):运行时错误
  • MLE(Memory Limit Exceeded):内存超限
  • PE(Presentation Error):输出格式错误

对OJ1004这种简单题来说,最常见的卡壳是WA和PE。WA多半是边界条件或数学公式出错,PE则往往是多打印了一个空格、少换了一行。很多教程只教你写代码,不教你解读判题测试报告,但报告其实是很好的反馈。比如它提示你在“脚数为奇数”这个用例上失败,基本可以确定是忘了判断(f-2h)的奇偶性。

2.2 输入输出格式的坑

先说输入。鸡兔同笼题目常见的输入方式有三种:

  1. 单组输入,一行两个整数
  2. 多组输入,读到EOF结束
  3. 多组输入,以特定终止标记结束,比如0 0

我见过很多新手写的代码是这样:

scanf("%d%d", &h, &f); printf("%d %d", chicken, rabbit);

如果是单组测试题,这没问题。但如果题目写的是“多组测试数据”,你只读一组,判题系统拿后面几组用例去跑,你的程序直接结束了,结果必然是WA。处理多组输入的标准姿势是:

  • C/C++:while (scanf("%d %d", &h, &f) != EOF)
  • Java:while (in.hasNextInt())
  • Python:用sys.stdin逐行读取

再说输出。OJ的输出比较是逐字符进行的,多一个空格、少一个换行都可能判错。鸡兔同笼的答案一般是先输出鸡的数量,再输出兔的数量,中间一个空格,最后换行。如果题目要求无解时输出“No answer”,那大小写和空格也要和题目描述完全一致,写成了“No Answer”照样WA。

2.3 边界条件的合法性判断

这题的边界条件,概括起来就是一个不等式组加上一个奇偶性判断。设头数为h、脚数为f:

  • 脚数不能少于所有动物都只有2条腿的情况,所以f必须大于等于2h
  • 脚数不能多于所有动物都有4条腿的情况,所以f必须小于等于4h
  • 因为鸡有2条腿,所以(f-2h)必须是偶数,否则兔子数量不是整数
  • 上面三个条件都满足时,兔子数(f-2h)/2自然非负,鸡数h-兔子也自然非负

举个例子:头3个,脚7只。按公式硬算,兔子=(7-6)/2=0.5,不是整数,这组数据实际上不可能存在。如果代码里不做取模判断,直接当整数输出,就会得到错误结果。所以核心判断可以写成:

if f < 2 * h or f > 4 * h or (f - 2 * h) % 2 != 0: print("No answer")

注意:有些题目会明确写“测试数据保证有解”,这种情况下你可以省略合法性判断直接算。但保险起见,多写一层判断通常不会影响正确性,也让你在遇到隐藏用例时更稳。

3. 实操过程与核心环节实现

3.1 C语言实现

C是OJ上最常见、也最能暴露细节的语言。完整代码可以这样写:

#include <stdio.h> int main() { int h, f; while (scanf("%d %d", &h, &f) != EOF) { int diff = f - 2 * h; if (f < 2 * h || f > 4 * h || diff % 2 != 0) { printf("No answer\n"); continue; } int rabbit = diff / 2; int chicken = h - rabbit; printf("%d %d\n", chicken, rabbit); } return 0; }

这里有两个细节值得说。第一,while(scanf(...) != EOF)可以同时兼容单组和多组输入。第二,diff先算出来,避免重复写表达式,也更容易阅读。至于整型选择,头和脚的数据范围一般不会太大,int够用;但如果你看到题目限定的头数范围很大,比如超过10万,就改用long long,防止乘法和减法溢出。

3.2 Java实现

Java在OJ上的痛点主要是类名和Scanner性能。以华为OJ这样的平台为例,提交的class名必须是Main,类里面不能带package声明。代码可以写成这样:

import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNextInt()) { int h = in.nextInt(); int f = in.nextInt(); if (f < 2 * h || f > 4 * h || ((f - 2 * h) & 1) == 1) { System.out.println("No answer"); continue; } int rabbit = (f - 2 * h) / 2; int chicken = h - rabbit; System.out.println(chicken + " " + rabbit); } } }

这里用& 1判断奇偶,其实和% 2 != 0完全等价,只是很多老程序员习惯这么写。hasNextInt()nextInt()配合,天然支持多组输入。如果数据量非常大,可以换成BufferedReader提速,但鸡兔同笼这种题数据量一般很小,Scanner完全够用。

3.3 Python实现

Python刷OJ,输入输出一定要分清input()sys.stdin。如果题目明确只有一组输入,用input()没问题;但如果要多组输入直到EOF,直接写input()读到底部会抛EOFError,所以最好用sys.stdin

import sys for line in sys.stdin: line = line.strip() if not line: continue h, f = map(int, line.split()) if f < 2 * h or f > 4 * h or (f - 2 * h) % 2 != 0: print("No answer") continue rabbit = (f - 2 * h) // 2 chicken = h - rabbit print(chicken, rabbit)

这里用整数除法//而不是/,是因为Python的/会得到浮点数,而OJ期望输出整数。line.strip()用来去掉可能的空行,如果某一行是空行就直接跳过,避免map(int, ...)在空字符串上报错。

3.4 自测用例设计

不管用什么语言,提交之前都建议先在本地构造一组测试数据。我常用的用例是这样:

编号输入 h f期望输出说明
13 82 1正常情况
21 40 1全是兔子
31 21 0全是鸡
43 7No answer腿数是奇数
53 5No answer脚太少,小于2h
62 9No answer脚太多,超过4h
70 00 0空笼子,看题目是否允许

第7个用例要看题目是否允许h=0。如果题目保证h>=1,这个用例就不必构造。把测试数据按行写到本地文件中,喂给程序,逐条对比输出,能覆盖绝大多数边界条件,再去OJ提交就不容易翻车。

3.5 为什么我建议三种语言都写一遍

这道题逻辑很简单,但我仍然建议初学者把C、Java、Python各写一遍。原因不是凑代码量,而是三种语言刚好覆盖三种典型输入风格:C教你理解EOF和指针层面的底层处理,Java逼你关注类名和Scanner的循环读取,Python教你用流式读取避免EOF异常。同一个逻辑在不同语言下的写法差异,恰恰是OJ刷题最需要适应的东西。以后你换平台做题,语言不熟、输入模板不熟,往往比题目本身更耗时间。

4. 常见问题与排查技巧实录

4.1 提交WA的高频原因

WA绝对是最常见的反馈。我统计过自己带过的学生提交记录,WA通常集中在下面几个原因:

  1. 边界条件漏判:最常见的是忘了腿数为奇数这个无解场景。比如h=3、f=7,按公式算兔子是0.5,程序直接输出错误数值。
  2. 公式系数套错:题目把“脚”换成“轮子”,自行车2轮、三轮车3轮,如果还按2和4算,结果必然不对。
  3. 输出格式不符:题目要求输出No answer,你写成了No Answer;题目要求先鸡后兔,你输出成先兔后鸡。
  4. 多组输入没处理:只读一次就结束,后续数据没有跑完。
  5. 多余输出或注释:某些老OJ对输出要求非常严格,代码里出现中文输出或调试注释也可能导致WA。

排查时不要急着改代码。先看判题测试报告显示的是哪类失败,再对照上面清单逐项检查。大多数时候,报告里已经暗示了失败用例的样子。

4.2 读懂判题测试报告

很多OJ会在提交结果显示“通过了X组测试中的Y组”,点击还能看到详细报告。以我熟悉的课程OJ为例,报告里通常会包含:

  • 测试输入,比如“头=3,脚=7”
  • 你的输出和标准输出的diff
  • 判定的错误类型

如果你看到标准输出是No answer,而你输出的是1 1,问题基本出在奇偶性判断。如果你看到所有输出内容都对,但反馈是PE,那就要检查每行末尾是否少换行、数字之间是否多空格。判题测试报告是定位问题的最直观材料,比瞎猜代码高效得多。

4.3 本地快速自测的小技巧

最后分享一个我非常习惯的做法。本地创建一个input.txt,把多组测试数据写进去:

3 8 1 4 1 2 3 7 3 5 2 9

然后运行程序时把文件内容作为标准输入:

  • C程序编译后用./a.out < input.txt
  • Java用java Main < input.txt
  • Python用python main.py < input.txt

对比输出是否符合预期。这样做的好处是,提交前你能肉眼确认多组输入、空行、大小写这些细节,还能一次性回归所有边界用例。等代码稳定后再把这个文件清理掉,不影响项目目录整洁。

4.4 一个真实的翻车案例复盘

有一回我在西科大OJ平台上帮同学看这道题,他的逻辑一眼看去没问题,兔子数、鸡数都算对了,但提交就是WA。我把他的代码拿来跑本地自测用例,全过。后来我干脆下载了判题测试报告,发现失败用例是一个特别大的头数范围。再回头看代码,他定义的是int h, f;,计算4 * h时发生了整数溢出,正确答案被截断成了负数,边界判断自然全乱了。改成long long之后一次通过。这个案例给我的教训是:别看题目简单,数据范围描述一定不能跳过,乘法溢出在简单的题里照样能要命。

5. 这道题给我的练习体会

刷题刷了很多年,回头看OJ1004这种“相当于鸡兔同笼问题”的题,反而觉得它最有教学价值。它小到不需要设计算法,但又大到逼你把输入输出、判题规则、边界条件全部摸清。我在实际练习中感受最深的是:很多人在简单题上花的时间其实不少,只是没花在看得见的维度。代码的核心逻辑就三行,真正决定能不能AC的是对细节的敬畏。

从这道题之后,我养成了几个固定习惯:第一,每道OJ题先读输入输出约束再动手写代码;第二,写完先用边界条件自测再提交;第三,遇到WA先拉判题测试报告,不瞎猜。第一次提交就AC不是靠运气,而是靠把可能的坑提前踩完。建议你也找自己学校或常用的OJ平台,比如西科大OJ平台、东华OJ,或者干脆去华为OJ上搜一道鸡兔同笼变体,把整条流程走一遍。等你真正读懂一份判题测试报告里的隐藏用例,OJ平台的判题逻辑在你眼里就不再是黑盒了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 18:11:41

聚类算法选型与实操:从K-Means到DBSCAN的完整指南

说句实在话&#xff0c;做数据这行当久了&#xff0c;聚类算法都快成条件反射了。拿到一批没有标签的数据&#xff0c;先跑个聚类看看形态&#xff0c;几乎成了常规动作。但问题也出在这——很多人一上来就 KMeans(n_clusters3) &#xff0c;跑完画个散点图就算交差&#xff…

作者头像 李华
网站建设 2026/9/9 18:11:19

AI浪潮下的中层岗位蒸发:从信息管道到价值锚点的生存法则

1. 我亲眼看到的“蒸发式”离职&#xff1a;岗位消失和裁员根本不是一回事先别急着把标题当成一个夸张的比喻。我最初接触到“AI 蒸发中层岗位”这个说法时&#xff0c;也以为它只是“裁员”的另一种文艺表达。直到今年上半年&#xff0c;我在短期内连续目睹了三起真实案例&…

作者头像 李华
网站建设 2026/9/9 18:09:42

UI自动化测试核心技能:元素定位与等待同步实战指南

测试这行干久了你会发现一个规律&#xff1a;不管你是用Selenium、Appium&#xff0c;还是后来冒出来的Playwright、Cypress&#xff0c;再换到带AI辅助的测试工具&#xff0c;日常执行失败的根因翻来覆去就那么几个——元素找不到、元素等不到、脚本跑一半因为定位或时机问题直…

作者头像 李华
网站建设 2026/9/9 18:08:49

家庭数据备份方案实战:三层架构、工具选型与恢复演练指南

开头先交代一下&#xff1a;这篇不是讲什么高大上的新玩意儿&#xff0c;而是把过去半年我自己折腾“家庭数据备份”这件事的完整记录整理了出来。起因很简单&#xff0c;硬盘里十年的照片、工作文档、攒的各种配置文件和插件&#xff0c;差点因为一次手滑全没了。从那之后我认…

作者头像 李华
网站建设 2026/9/9 18:08:43

AI重拓扑插件完整工作流:从高模到低模的自动化实战指南

这次我们来看 AI 重拓扑插件的完整工作流。对做 3D 建模、游戏资产、数字人和产品渲染的开发者来说&#xff0c;重拓扑一直是高模转低模里最耗时的环节。手动拓扑一圈一圈地连线&#xff0c;遇到布线密度不够、UV 拉伸、转角折痕不对&#xff0c;又得重来一遍。AI 重拓扑插件要…

作者头像 李华
网站建设 2026/9/9 18:06:33

CUDA调试实战:用Compute Sanitizer定位显存越界与数据竞争

我接手过一个让人印象深刻的“幽灵Bug”&#xff1a;一个图像卷积kernel&#xff0c;跑小规模测试完全正常&#xff0c;放到生产数据上跑几分钟就随机崩溃&#xff0c;有时甚至算出明显错误的结果但进程不退出。项目组前面换了好几种排查思路&#xff0c;打印、加锁、换数据分块…

作者头像 李华