东方博宜OJ 1201-1210题解:C++基础算法详解与提交避坑指南
2026/9/13 11:05:01 网站建设 项目流程

东方博宜OJ的1201-1210,是很多刚接触在线评测系统的同学都会搜的一个连续题号。我当初也搜过,抄过不少代码,但真正让我把这十题吃透的,是后来一篇篇重新写、重新调的过程。这篇文章把我整理的1201-1210答案、每题的考点和一份可以直接交的C++代码都放出来,同时也会讲清楚每题背后的原理,以及提交评测时最容易翻车的地方。不管你是急着交作业,还是想把基础打牢,都能从这里找到有用的东西。

1. 先看这十题到底考什么:考点分布与刷题顺序

题号这个东西,不同OJ之间经常对不上,东方博宜OJ自己的题库顺序也不是永远固定。所以与其死记题号,不如先认清1201-1210这十题在知识模块上到底覆盖了什么。我刷的时候整理过一张表,方便你对号入座:

题号常见题目特征核心考点
1201三个整数求最大值分支结构、条件比较
1202求1到n的累加和for循环、数学公式
1203判断某年是否为闰年逻辑运算符、优先级
1204打印直角三角形星号图案循环嵌套、输出格式
1205求斐波那契数列第n项递推、递归优化
1206将数组元素逆序输出一维数组、倒序遍历
1207统计字符串中大写字母个数字符串遍历、ASCII码
1208求两个正整数最大公约数辗转相除法
1209判断一个数是否为素数循环边界、平方根优化
1210输出所有三位水仙花数拆位、枚举、数学运算

如果你打开自己平台上的题库,发现某个题号和这里对不上,不用慌,认准题目特征而不是死记编号。这十题整体偏基础,但基础不等于简单,很多人在1209、1210上第一次体会到“明明逻辑对,提交却WA”的崩溃。

1.1 从送分题到综合题的进阶逻辑

前五题基本是单知识点,后五题开始把多个知识点组合起来。1203考条件运算符的组合优先级,1204考循环嵌套里的换行位置,1208考数学算法,1210考拆位运算。这些能力是后面刷排序、查找、模拟题的地基。我当时是先做一遍拿到AC,再重新不看代码手写一遍,两种感受完全不一样。手写一遍才会发现自己对for循环边界、数组下标这些细节到底有没有真懂。

1.2 为什么我建议先自己写,再对答案

我知道搜“OJ答案”的大部分人就是想快速交作业,这个我不反对,但如果你想省事,反而更应该先把题目复制出来自己跑一遍。哪怕写得很烂,只要提交通过,你至少知道了题目要求;这时候再看下方的参考代码,会明显看到自己的不足。直接抄代码的问题在于:评测系统不会提醒你哪里没懂,下次遇到类似题,你还是只会复制粘贴。这十题我后来给其他同学讲的时候,发现很多错误其实都出在“以为自己会了”的地方。

2. 1201-1205题解:分支、循环与数列

这部分我把前五题的代码和思路拆开讲。代码用C++写,东方博宜OJ是支持这种写法的,其他语言思路也一样,把语法换掉就行。

2.1 1201:三个整数求最大值

题目特征很直接:给你三个整数,让你输出其中最大的一个。常规做法是分支结构,写起来就是两两比较。

#include <iostream> using namespace std; int main() { int a, b, c; cin >> a >> b >> c; if (a >= b && a >= c) { cout << a << endl; } else if (b >= a && b >= c) { cout << b << endl; } else { cout << c << endl; } return 0; }

如果你不想写这么长的逻辑,直接用STL的max函数更省事:

#include <iostream> #include <algorithm> using namespace std; int main() { int a, b, c; cin >> a >> b >> c; cout << max(a, max(b, c)) << endl; return 0; }

这里要注意的是比较运算符的等号。用>=还是>对最终输出没有影响,因为三个数相等时,无论进入哪个分支输出的都一样。但如果你后面要处理“输出第几个数”这类问题,等号处理就要小心。另外,&&的优先级比||高,如果加了括号更保险,很多新手就是在这种地方漏括号导致逻辑混乱。

2.2 1202:求1到n的累加和

这题本质是等差数列求和。你既可以写for循环从头加到尾,也可以直接用数学公式n * (n + 1) / 2。我推荐先把循环写熟,因为后面很多题都需要循环思维。

#include <iostream> using namespace std; int main() { int n; cin >> n; long long sum = 0; for (int i = 1; i <= n; i++) { sum += i; } cout << sum << endl; return 0; }

为什么用long long而不是int?因为n如果稍微大一点,累加和很容易超过int的21亿上限。比如n等于70000,结果已经突破24亿了。很多人在这种送分题上WA,就是栽在数据类型上。如果你用公式法,也要注意先把n强转成long long再乘,避免两个int相乘时溢出。

2.3 1203:闰年判断

闰年的定义是:能被4整除但不能被100整除,或者能被400整除。题目通常不会只给一个条件,所以组合逻辑是这里的核心考点。

#include <iostream> using namespace std; int main() { int year; cin >> year; if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { cout << "YES" << endl; } else { cout << "NO" << endl; } return 0; }

%取余运算的优先级和==!=这类比较运算符不太一样,比较运算符又比&&||高。为了避免踩坑,最好把每个条件用括号包起来,养成习惯。还有一个常见错误是只判断year % 4 == 0,结果1900年这种年份就判断错了。1900能被4整除,但不能被400整除,所以不是闰年。

2.4 1204:打印直角三角形星号图案

题目一般要求输入一个n,输出n行星号,第一行1个,第二行2个,以此类推。这题的考点是循环嵌套,内层循环控制每行星号个数,外层循环控制行数。

#include <iostream> using namespace std; int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++) { cout << "*"; } cout << endl; } return 0; }

最容易错的地方是把cout << endl放在内层循环里,那样每打一个星号就换一行,图案全部乱掉。另外,有些题目会要求靠右打印,也就是前面加空格,这时候内层要变成两个循环:先打印空格,再打印星号。你在OJ上看到“直角三角形”四个字时,一定先看样例输出,确定是左对齐还是右对齐。

2.5 1205:斐波那契数列第n项

斐波那契数列从第1项开始通常是1、1、2、3、5、8……题目可能让你求第n项。网上很多教程喜欢用递归,但递归在这个场景下效率很差,计算第50项可能就卡住。用递推是最稳的。

#include <iostream> using namespace std; int main() { int n; cin >> n; if (n == 1 || n == 2) { cout << 1 << endl; return 0; } long long a = 1, b = 1, c; for (int i = 3; i <= n; i++) { c = a + b; a = b; b = c; } cout << c << endl; return 0; }

这题的考点不只是“知道递推公式”,更关键的是理解滚动更新的过程:每算出一个新数,就把原来的第二项变成新的第一项,把新数变成第二项。如果你直接写a = b; b = a + b;,会因为b被更新过导致结果错误。这种细节在初学阶段非常容易忽略。

3. 1206-1210题解:数组、字符串与数学思维

后五题从单点知识变成了组合应用。数组、字符串、数学算法,每个都是后面刷题的基本功。

3.1 1206:数组元素逆序输出

题目一般先输入n,再输入n个整数,最后要求把这n个数逆序输出。最直观的方法是开一个新数组倒着存,但完全不必要,直接倒序遍历原数组即可。

#include <iostream> using namespace std; int main() { int n; cin >> n; int a[105]; for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = n - 1; i >= 0; i--) { cout << a[i] << " "; } cout << endl; return 0; }

数组长度我习惯开到105,比题目给的上限多一点,防止越界。逆序输出时最常犯的错是循环从i = n开始,把数组最后一个元素后面的垃圾值打出来。另外,这里会在每个数后面输出一个空格,东方博宜OJ一般能接受行尾空格,但如果遇到严格比对的题目,你需要用i == 0的特判来避免最后的空格。

3.2 1207:统计字符串中大写字母个数

这题需要遍历字符串,并对每个字符判断是否属于大写字母。如果题目给的字符串不含空格,直接用cin >> s就行;如果包含空格,必须用getline读取整行。

#include <iostream> #include <string> using namespace std; int main() { string s; getline(cin, s); int cnt = 0; for (int i = 0; i < s.length(); i++) { if (s[i] >= 'A' && s[i] <= 'Z') { cnt++; } } cout << cnt << endl; return 0; }

这里用字符比较而不是ASCII码值比较,读起来更清晰。如果你用的是cin >> s,要小心输入缓冲区里残留的换行符。比如前面读了一个整数n,后面用getline读字符串,会先读到一个空串,这种情况需要在整数读完后用cin.ignore()把换行清掉。这个坑在OJ里非常经典。

3.3 1208:最大公约数

求两个正整数的最大公约数,最经典也最保险的是辗转相除法。原理是:两个数的最大公约数等于较小数和两数相除余数的最大公约数。代码很短,但理解起来需要点时间。

#include <iostream> using namespace std; int main() { int a, b; cin >> a >> b; while (b != 0) { int r = a % b; a = b; b = r; } cout << a << endl; return 0; }

如果你习惯用函数,也可以写成递归版:

int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }

但注意递归版要防止栈溢出,好在这里的数不会太大。很多人在纸上画流程觉得“懂了”,一写代码就把a % b换成a / b,结果完全不对。记住:先取余,再交换,和斐波那契的滚动更新是同一个套路。

3.4 1209:判断素数

素数的定义是只能被1和自身整除的正整数,1不是素数。最笨的办法是从2检查到n-1,但这样效率低,优化到根号n就够了。

#include <iostream> #include <cmath> using namespace std; int main() { int n; cin >> n; if (n < 2) { cout << "NO" << endl; return 0; } bool flag = true; for (int i = 2; i * i <= n; i++) { if (n % i == 0) { flag = false; break; } } if (flag) { cout << "YES" << endl; } else { cout << "NO" << endl; } return 0; }

i * i <= ni <= sqrt(n)好在:不会因为浮点数精度出问题,也不用额外引入cmath。不过乘法在i很大时也可能溢出,更稳妥的写法是i <= n / i。这题很多人的错误点是没有特判n小于2的情况,比如输入1,应该输出NO,结果因为循环没进去直接输出YES了。边界条件永远是OJ最阴的地方。

3.5 1210:水仙花数

水仙花数指的是一个三位数,其各位数字的立方和等于它本身,比如153等于1的立方加5的立方加3的立方。题目通常要求输出所有满足条件的三位数。

#include <iostream> using namespace std; int main() { for (int n = 100; n <= 999; n++) { int a = n / 100; int b = n / 10 % 10; int c = n % 10; if (a * a * a + b * b * b + c * c * c == n) { cout << n << endl; } } return 0; }

拆位数是这题的核心,三个表达式分别取出百位、十位和个位。n / 10 % 10的意思是先整除10去掉个位,再对10取余得到十位,这个顺序不能写反。如果你只会用字符串转数字的方式去做,也能做,但比赛里尽量用算术方式,更快更稳。

4. 提交OJ时的隐藏坑:格式、换行、超时

我从1201刷到1210,发现真正让新手反复提交的往往不是算法本身,而是评测系统那些“没说出口”的规矩。下面这几个坑,几乎每个人都踩过。

4.1 输出格式:多一个空格都WA

东方博宜OJ这类在线评测系统,比对输出时通常是逐字符比对。也就是说,答案中多一个空格、少一个换行,都会被判定为答案错误。比如打印数组时,很多人习惯最后一个数后面也加空格,有的题目会判错。稳妥的做法是:遍历时如果i > 0,先输出一个空格,再输出当前元素,这样行尾就不会有多余空格。

for (int i = 0; i < n; i++) { if (i > 0) cout << " "; cout << a[i]; }

4.2 换行与缓冲区残留

输入数据可能在多行,也可能在一行。cin会忽略空格和换行,所以大部分整数读取不需要特别处理。但一旦涉及字符串,尤其使用getline,就要小心前面有没有残留换行符。我见过最典型的情况是:先读n,再用getline读字符串,结果s是个空串。解决办法就是在中间加一行cin.ignore();。这个操作虽然不起眼,但比赛中不知道卡了多少人。

4.3 数据范围:int溢出总是在你最没防备时出现

1202累加和的溢出我说过一次,但这十道题里还有斐波那契、水仙花数这类题目。拿斐波那契来说,第46项左右就超过int范围了,如果题目给的n比较大,你还用int存,那结果就是错的。建议在不确定时,直接声明成long long,至少少一个隐患。判断素数的循环边界也要注意,i * i在i到10万时是10的10次方,已经超过int,所以更推荐i <= n / i的写法。

4.4 数组开小导致越界

数组下标越界在OJ上是运行时错误,有时候会直接显示“Runtime Error”,有时候则因为未定义行为导致随机WA。我习惯把数组长度比题目上限多开5到10个,比如题目说n最大100,我就开105。这不会浪费多少内存,但能避免很多奇怪的问题。

5. 刷完这十题后,你应该继续练什么

1201-1210只是起点,但也是很好的起点。把这几题彻底弄懂之后,接下来的路会顺很多。我觉得比多刷题更重要的,是建立自己的“刷题反馈循环”。

5.1 从“看答案”到“写题解”

我看过太多同学把代码复制进OJ,AC后就关掉页面,过几天回来照样不会。我自己后来改变做法:每AC一道题,就用自己的话把思路写下来,不写代码,只写“先做什么,再做什么,为什么这么做”。等过三天再翻出来,看着笔记重写代码。这样做一次,比抄十道题都有用。你现在搜到这篇答案,也可以先AC,然后合上代码自己重新敲一遍,哪怕敲不出来,也远比直接粘贴有收获。

5.2 举一反三:把1201-1210改编成新题

这十题能扩展出很多变体。比如1204打印直角三角形,改成打印等腰三角形、菱形、空心三角形;1205斐波那契改成跳台阶问题;1209判断素数改成统计区间内素数个数。这些变体在东方博宜OJ上都有类似题目,比如很常见的“空心正方形”就是图形题的进阶版。你在搜答案时如果看到这些热词,不用觉得陌生,它们全是1201-1210这些基础考点的变形。

5.3 补上算法常识:复杂度、边界、调试

刷完这十题,你应该开始接触“时间复杂度”和“空间复杂度”这两个概念。1205用递归会超时,1209用2n-1枚举在大数时会很慢,这些都是复杂度问题。理解复杂度之后,写代码前就会多想一步:这个数据范围,我的算法能不能在1秒内跑完?这种意识越早建立越好。

最后再分享一个我调试时常用的笨办法:如果提交WA,不要盯着代码发呆,回来看样例输出。把样例输入复制进去,看自己的输出和样例是否完全一致,包括空格和换行;然后自己造几组边界数据,比如0、1、负数、最大值。这十道题虽然基础,但把边界吃透,后面刷任何题都会受益。别急着跳过,一个个试过来,你会发现在OJ上“答案”不是一个结果,而是一套不断调试的方法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询