
如果你正在准备互联网公司的研发岗实习面试网易2016实习研发工程师编程题这套题大概率绕不开。2016年前后“校招笔试线上化”刚好走到一个转折点网易把这套题目放到在线笔试平台上题目量不大、难度梯度合理很快就在牛客网和各种面经帖里流传开来成了当时练手最狠的一套题。它不考冷门算法数清楚知识点就是数组、排序、贪心再加上一堆藏在细节里的边界条件。适合两类人一类是马上要参加笔试的应届生另一类是刷题刷到麻木、想找套题检验基本功的工程师。这篇文章我不打算把三行代码甩给你就完事我会把每道题怎么想、怎么写、为什么这么写、在哪里容易翻车全部摊开讲。1. 2016年这套题究竟在考什么1.1 三道题背后的三种核心能力网上流传的网易2016实习研发工程师编程题不同人回忆的版本略有出入但主流版本稳定在三个题目洗牌、奖学金、路灯。这三道题不是随机拼凑的它们分别对应了研发岗笔试里最常出现的三种能力维度。第一道洗牌考的是模拟能力。题目给你一段完整的流程描述要求你把这个过程原封不动地用代码再现。这类题不考智商也不考算法设计它考的是你能否耐心地把“自然语言”翻译成“代码逻辑”尤其考下标计算和循环边界。很多同学算法题能做出难题反而在模拟题上栽跟头就是因为不重视这种基本功。第二道奖学金考的是贪心策略。在资源有限的前提下怎么用最小的代价达成目标。贪心是面试里出现频率最高的算法思想之一因为它在真实业务中无处不在预算有限怎么做渠道投放、服务器资源有限怎么排任务、库存有限怎么分配赠品。它的难点不是写代码而是判断“贪心是否成立”并给出让人信服的理由。第三道路灯考的是排序加边界处理。题目本身不复杂排序之后扫一遍数组就能出结果但它有一个非常典型的细节坑你必须同时照顾到首尾两个端点。这种“思路五分钟、边界两小时”的题恰恰是笔试里最能拉开区分度的类型。三道题合在一起其实就是一套完整的研发基本功检验组合。1.2 难度定位基础但不送分网易这套题的整体难度放在2025年回头看属于“中等偏下”。没有线段树没有动态规划没有字符串匹配更没有图论。凡是参加过几次校招笔试的人都会觉得这套题的算法门槛很低。但它的难点从来不在算法而在“能不能一次写对”。我自己的感受是这套题最大的杀伤力在于细节。比如洗牌题洗牌方向是“左半张先出”还是“右半张先出”不同版本题目描述不一样想当然就会做反。比如路灯题路灯数量可能为1路的两端有没有灯、要不要覆盖到端点都是送命题。再比如奖学金题金币总数可能远超int范围用int存结果直接溢出。所以这套题看起来很基础但想全部AC并不轻松。从当年牛客网讨论区的反馈来看很多人的分数集中在通过1到2题三题全过的比例并不高。它本质上是一套“看你会不会认真读题、会不会严谨处理边界”的试卷而不是“看你会不会高级算法”的试卷。1.3 为什么过了这么多年还值得刷我知道很多人会有疑问2016年的题现在都2025年了还有必要看吗我的回答是有必要而且非常有必要。原因很简单这套题覆盖的基础能力和现在校招笔试考查的核心能力几乎没有变化。现在的大厂笔试确实更卷了题目更偏向LeetCode中高难度题型但模拟、贪心、排序、边界处理这些基础盘依然是所有难题的地基。你去做任何一套现在的笔试题拆到最后底层能力不会超出这几个范畴。而且这套题比LeetCode单题更有价值的地方在于它是一个完整的“笔试场景”需要你自己读题、自己设计输入输出、自己跑样例、自己排查边界。这种综合性训练是零散刷题给不了的。把它当成校招笔试的“入门自测题”再合适不过。2. 三道经典题逐个拆解思路、解法与现场实现2.1 洗牌纯粹拼下标计算的模拟题洗牌这道题我印象太深了。第一次写的时候我直接栽在下标上。题目大意是这样的有一个长度为2n的数组表示一叠牌。洗牌一次的过程是先把数组分成左右两半各n张然后左右交替重新排列。比如数组是[1,2,3,4,5,6]n等于3洗一次牌左半是[1,2,3]右半是[4,5,6]交替排列之后得到[1,4,2,5,3,6]。现在要求重复洗k次输出最终顺序。拿到这个题第一个想法就是直接模拟。定义一个新数组遍历左半部分把左半第i张放到2*i的位置把右半第i张放到2*i1的位置一轮洗牌结束之后把新数组复制回原数组重复k次。这里的n和k一般都在几十到几百时间复杂度O(kn)空间复杂度O(n)完全够用。#include cstdio #include vector using namespace std; int main() { int T; scanf(%d, T); while (T--) { int n, k; scanf(%d%d, n, k); vectorint a(2 * n); for (int i 0; i 2 * n; i) { scanf(%d, a[i]); } while (k--) { vectorint b(2 * n); for (int i 0; i n; i) { b[2 * i] a[i]; b[2 * i 1] a[n i]; } a.swap(b); } for (int i 0; i 2 * n; i) { if (i) printf( ); printf(%d, a[i]); } printf(\n); } return 0; }这里我想强调一个很容易被忽略的细节左右交替的先后顺序。有的题面描述是“先取左半第一张再取右半第一张”也就是我上面代码里的写法但如果你遇到的版本是“先取右半第一张”那b[2*i]和b[2*i1]的赋值就要反过来。笔试的时候不能凭记忆必须回到题面一字一字确认。就这一个顺序问题当年不知道卡掉了多少人。这道题的进阶做法是找索引映射关系直接算某一轮之后每张牌去到了哪个位置从而把复杂度优化到O(k)。但笔试场景下我不建议这么做除非n特别大。原因有两个一是直接模拟的代码最短越短的代码越不容易出错二是笔试时间紧张与其折腾映射公式不如直接用最简单可靠的办法把分拿稳。2.2 奖学金贪心策略为什么能保证最优奖学金这道题不同人回忆的细节不完全一样但核心结构很稳定。大致意思是有n门课每门课已经有一个当前成绩课程有满分上限通常按100分算。如果你想拿到奖学金所有课程的平均分不能低于某个目标值avg。现在可以花金币给某门课提高成绩每提高1分需要消耗一定数量的金币不同课程的成本可能不同。问最少需要花多少金币。这类题的标准解法是贪心把所有课程按照“提高1分需要花费的金币数”从小到大排序优先给成本最低的课补分直到平均分达标。为什么贪心在这里是对的因为目标是补足一个固定的总分缺口而每个“单位分数”之间是等价的可以拆开购买。这门课补1分、那门课补1分本质上没有区别区别只在价格。所以每次选择当前价格最低的“1分”去购买就能保证总花费最小。这跟你去超市买东西一个道理同样的商品哪家便宜就在哪家买钱花得就少。它本质上是一个分数背包问题因为分数可以按1分为单位无限细分所以贪心成立如果换成每个物品只能整体选或不选那就是01背包贪心就不一定对了。代码实现上要注意几个点。第一需要补的总分是n * avg - 当前总分如果这个值小于等于0直接输出0不用补。第二每门课能补的分数是满分 - 当前成绩不能补过头。第三金币总数要用long long或int64来存因为n、avg、金币单价都乘起来之后int很容易溢出。def min_coins(n, avg, courses): # courses: [(cost_per_point, current_score, max_score)] missing n * avg - sum(c[1] for c in courses) if missing 0: return 0 courses.sort(keylambda x: x[0]) coins 0 for cost, score, max_score in courses: if missing 0: break can_add max_score - score add min(can_add, missing) coins add * cost missing - add return coins我在实际写这道题的时候犯过一个低级错误排序之后没有判断剩余可补分数直接把can_add全补上去了导致多算了很多金币。笔试这种环境下越简单的题越容易因为“想当然”出错。每次循环结束前一定要确认missing到底减少了多少最好在草稿纸上先推一组小数据再动手写。2.3 路灯排序之后真正的坑在端点路灯这道题题目描述大概是有一条长度为L的路坐标从0到L路上有若干个路灯每个路灯的坐标已知。路灯的照射半径是R要求整条路的所有点都能被至少一个路灯照亮求R的最小值。第一眼看过去最简单直观的思路就是排序。把所有路灯坐标排序之后两个相邻路灯之间的路段需要两边的灯各覆盖一半才能保证中间那段也被照亮。所以相邻路灯间距的一半就是一个候选答案。但很多人会漏掉两个关键端点第一个路灯到起点0的距离以及最后一个路灯到终点L的距离。这两段路上只有一侧有灯所以必须由端点那盏灯单独覆盖半径至少等于这个距离。所以完整答案是三个候选值里的最大值第一个路灯的坐标也就是从0到第一个灯的距离L减去最后一个路灯坐标也就是最后一个灯到L的距离相邻路灯之间最大间距的一半。#include cstdio #include algorithm using namespace std; int main() { int n, L; while (scanf(%d%d, n, L) ! EOF) { int a[1005]; for (int i 0; i n; i) { scanf(%d, a[i]); } sort(a, a n); int gap2 max(2 * a[0], 2 * (L - a[n - 1])); for (int i 1; i n; i) { gap2 max(gap2, a[i] - a[i - 1]); } printf(%.2f\n, gap2 / 2.0); } return 0; }这里有一个实战技巧因为最后答案要输出两位小数而计算过程中涉及“半”这种除法我习惯把所有距离都乘以2用整数比较大小最后再统一除以2。这样能完全避开浮点数比较误差的问题也不用担心printf的精度问题。比如两个路灯距离是5乘以2之后是10另一个端点距离是6乘以2之后是12直接整数比较最后输出12 / 2.0得到6.00干净利落。另外还要注意输入里的路灯坐标可能是无序的也可能有重复值排序之后一定要去重或者接受重复坐标的存在。重复坐标不会影响正确性因为相邻距离为0不会成为最大值但如果你写的是“坐标减1”之类的逻辑就可能在重复坐标上翻车。2.4 把三道题放一起看笔试的共性逻辑三道题晾在这里你会发现它们有一个共同的特点算法层面都很朴素但每个题都藏着一两个“语言陷阱”或“边界陷阱”。洗牌要你确认交替方向路灯要你确认端点奖学金要你确认成本和上限。这不是偶然而是笔试出题人有意为之。HR和面试官真正想筛选的不是谁见过更多高级算法而是谁能在有限时间里把一道简单题完整做对。所以如果你想从这套题里提炼一套复用方法论我建议按这个顺序来先读题三遍把题目里的每一个数字约束和操作步骤都圈出来再在草稿纸上手动跑一遍样例确认自己对流程的理解没有偏差最后再动手写代码写完立刻构造边界用例自测。这套流程应对任何基础题都管用。3. 笔试现场的全流程从审题到调试3.1 在线笔试环境的隐形门槛2016年前后校招笔试正在从线下纸质试卷转向线上OJ网易当时用的就是牛客网这类平台。这种平台有一个特点核心代码模式不是。它要求你提交一个完整的可运行程序自己处理输入输出也就是我们常说的ACM模式。这意味着什么意味着你不仅要写出算法逻辑还要正确解析输入格式可能有多组测试用例需要循环读取输出格式也要严格匹配多一个空格、少一个换行都有可能被判错。LeetCode刷习惯了的人第一次做这种题会非常不适应因为LeetCode把输入输出都帮你封装好了你只需要实现一个函数。但笔试现场没有这层封装。以洗牌题为例输入可能是先给一个T表示有T组测试数据然后每组先给n和k再给2n个数。如果你只处理了一组就跑完了后面所有测试用例都读不到等来的就是“通过0%”。正确的做法是外层套一层while(T--)或者用while(scanf(...) ! EOF)循环处理直到输入结束。这一层逻辑写对至少能保住基础分。3.2 拿到三题后的15分钟怎么分配我个人的习惯是拿到卷子先不着急写代码花5分钟把三道题全部通读一遍。重点看三样东西数据范围、输入格式、输出格式。数据范围决定你用不用long long输入格式决定你写不写循环输出格式决定你有没有保留两位小数。这三样看漏任何一个后面都是灾难。然后按难度排序。以这套题为例路灯和洗牌属于“模拟排序”的简单题奖学金属于“贪心”的中档题。如果是我我会先写路灯再写洗牌最后写奖学金。原因很简单路灯的代码最短思路最直接容易快速拿分洗牌虽然也简单但下标细节多需要留足时间慢慢调奖学金涉及排序加累加逻辑上最容易出现“差一点错”的情况适合放在最后专注处理。这里有个反常识的建议不要一上来就做你“感觉最简单”的题而是先看一眼所有题然后从“代码量最少”的题开始。因为笔试时间有限先把确定性高的分拿到手后面才有余裕啃硬骨头。3.3 写代码时的自检清单写代码的过程本身需要不断对照一份心理清单。我总结下来核心就是四条。第一数组下标。是从0开始还是从1开始题目给的坐标范围是否直接对应数组下标这个在洗牌题里特别明显左右半的边界划分是[0, n)和[n, 2n)如果你习惯写立刻就会数组越界。第二数据类型。所有可能累乘累加的变量一律默认用long long。奖学金题里n最大可能上万平均分目标接近满分金币单价再上百相乘之后轻松突破int的21亿上限。用int存结果样例也许能过一上大数据就溢出。第三多组输入的状态重置。全局变量、累加器、标志位每一组数据开始前都必须重新初始化。很多人第一组样例过了第二组开始答案莫名其妙错就是因为忘了重置。第四输出格式。字段之间是空格还是换行最后一行有没有多余空格浮点数要求几位小数这些细节在OJ判题里一个字都不能差。一个很土但有效的办法把题目给的输入样例和输出样例完整复制到程序里跑一遍逐字符对比。3.4 用最小用例和边界用例做最后自测代码写完、样例通过不代表万事大吉。真正的考验是边界用例。以这三道题为例我建议每个题至少补测三组数据。洗牌题测n1, k1数组长度只有2看左右交替是否还能正确执行再测k0或k很大的情况确保循环不会死循环。路灯题测只有一个路灯的情况此时答案是max(路灯坐标, L - 路灯坐标)很多人的代码在只有一个点时相邻路灯循环根本不会执行如果端点逻辑没写好答案就是错的。奖学金题测已经达到目标平均分的情况应该输出0再测所有课程都补到满分仍达不到目标的情况虽然题目可能保证有解但万一没保证你的代码要能正确处理而不是死循环。这三组自测全部通过这道题才算真正做完。笔试里有个残酷的现实部分正确不给分边界不过等于没做。多花两分钟自测可能省下的是一整年的遗憾。4. 实操中容易踩的坑与排查技巧实录4.1 高频报错与评分异常的速查表我把这些年带新人时遇到的高频问题整理成一张速查表遇到类似现象可以直接对照排查。现象可能原因修复方向洗牌输出顺序和样例完全相反左右半堆交替顺序写反回到题面确认先取哪一叠洗牌答案在n较大时混乱数组下标越界可能写成了 n检查左右半边界是[0,n)和[n,2n)路灯答案比预期小漏了起点和终点两端距离答案取三者的最大值起点、终点、相邻间隔/2路灯输出多了很多小数直接用浮点运算且精度丢失坐标乘以2用整数比较最后除以2奖学金金币数超出int范围累加结果溢出改用long long并把中间变量一并改掉只通过第一组样例后面全错多组输入循环没有写或状态没重置外层加while循环每组开始时初始化所有变量样例全对但提交0分输出格式问题比如多了空格或换行逐字符对比输出样例注意最后一行这张表里的每一行都是我或者身边人真实踩过的坑不是凭空编的。笔试出错的规律永远惊人的一致不是不会而是粗心。4.2 一次真实的排查过程复盘我自己在练习这套题时遇到过一件特别有意思的事。路灯这道题我本地测试样例全过提交上去就是0分。反复看了好几遍代码逻辑上实在找不出问题。后来我做了个很笨的操作——把题目给的输入样例连同输出样例一起复制到本地用diff工具逐字符比对。结果发现我的程序输出是2.00题目要求的是2.00看起来一模一样但我的输出末尾多了一个空格。问题出在printf的写法上。我在输出每个数字之后都习惯性加了一个空格最后一位后面也加了。OJ判题时把“多空格”视为格式错误直接给0分。这个错误后来让我养成了一个习惯所有涉及输出的代码循环里用if (i) printf( )这样的方式控制空格而不是在数字后面无脑跟空格。这个细节在洗牌题和路灯题里都很关键。还有一个排查技巧是“二分注释法”。当代码逻辑比较复杂、不知道怎么定位问题时先把一半代码注释掉看程序行为是否变化。如果注释掉前半段程序就正常说明问题在前半段如果程序依旧报错说明问题在后半段。反复几次就能把问题收敛到很小的范围。这个方法在笔试现场可能没时间用但在平时练习时非常高效。4.3 “样例过了但被判错”的几种常见来源网上讨论区里经常能看到有人发帖“样例过了为什么0分”这类问题十个里有八个是下面几种来源。第一理解偏差。题目描述里明明写了“洗牌k次”你只洗了k-1次题目写了“输出保留两位小数”你直接输出了整数。这些都属于没有逐字读题。我的建议是把题面里的每个数字、每个动词都当作约束条件在草稿纸上圈出来一个都不能漏。第二输入处理不完整。题目可能有多组测试数据但你只处理了一组或者处理完一组之后没有正确跳过空行。用cin 或scanf这类流式读取时空白字符空格、换行、Tab都会自动跳过通常没问题但如果你混用getline和cin 就可能出现“读了空行”的情况。建议要么全用流式读取要么全用getline再自行split不要混用。第三边界数据没有处理。路灯只有一个、奖学金目标已经达成、洗牌次数为0这类“极端但合法”的输入最容易暴露出平时没考虑到的逻辑漏洞。平时练习时养成构造边界用例的习惯笔试时就会条件反射地检查这些位置。第四浮点数比较的坑。路灯题如果用double计算再直接或比较可能因为精度问题得到错误结果。避免它的最彻底的办法就是我在2.3节里提到的整数化方案把坐标乘以2所有比较用整数完成最后再转成小数输出。5. 这套题放在今天还能怎么用5.1 从2016到现在笔试题型发生了什么变化2016年的校招笔试整体上还处在一个“考基本功”的阶段。题目范围窄、难度适中、很少出现特别偏的算法。到了2025年大厂笔试的风格已经明显变化题量增加、难度提升、动态规划和图论成为常客有些公司甚至加入了“场景设计题”和“系统设计题”。看起来变化巨大但有一个事实始终没变——简单题依然是筛选的第一道关卡。现在的大厂笔试通常也是先把简单题放在前面用来过滤连基本功都不扎实的候选人。如果你在模拟、排序、贪心、边界处理这些基础点上频繁翻车后面的难题根本没机会展示。所以网易这套2016年的题放在今天依然是最合适的“基本功体检工具”。花一个下午把它全部AC并且确保自己能把每道题的边界情况讲清楚比盲目刷几十道LeetCode难题更有价值。5.2 三道题的现代变体与延伸训练如果你把这套题吃透了完全可以把它延伸到现代题目里形成一套举一反三的练习路径。洗牌题延伸出去就是各类“数组重排”问题。LeetCode 1470重新排列数组几乎是洗牌题的简化版直接给出交叉规则让你实现。再进阶一点LeetCode 384打乱数组涉及随机洗牌就必须用Fisher-Yates算法那又是另一个层次的东西了。奖学金题延伸出去是典型的“贪心排序”组合。类似题目包括LeetCode 455分发饼干、LeetCode 135分发糖果、以及各种“最小代价达到目标”的变体。做这类题时重点练习的是“证明贪心正确性”的能力为什么局部最优不会影响全局最优。路灯题延伸出去是区间覆盖和区间合并问题。LeetCode 56合并区间、LeetCode 452用最少数量的箭引爆气球、以及经典的路灯改造问题。这类题的共同点都是排序之后处理端点边界情况永远是重心。5.3 再来一次我会怎么准备这套题如果让我以现在的经验回到当年重新准备这套题我会做三件不一样的事。第一我会把每道题的“一句话考点”用白纸写下来。洗牌题是“模拟时先确定位置公式”奖学金题是“按单价排序的贪心”路灯题是“端点与间隔取最大”。别小看这一句话总结它逼着你把整道题的逻辑压缩成可复用的模式下次遇到类似题能更快定位思路。第二我会在写代码之前先在草稿纸上手动跑一遍输入样例写出每一步的中间结果。这样可以尽早发现对题面理解的偏差而不是等代码写完、提交报错了再回头改。手动跑样例还能帮你确认输出格式尤其是在需要保留小数位时提前知道答案长什么样。第三我会专门花20分钟做“输入输出专项训练”。网易这套题是ACM模式和现在很多公司笔试一致输入输出处理不好算法再对也是零分。我会练习while(scanf(...) ! EOF)、while(cin n k)、Python的sys.stdin.read().split()这三种不同风格的读取方式确保自己不会在读数据上卡壳。回过头看这套题给我最大的启发不是某个算法而是“基础题必须一遍过”这句话的含金量。笔试不是比谁会的最难而是比谁在有限时间内犯的错最少。网易2016实习研发工程师编程题就是检验你犯错频率的最好试金石。如果你愿意花一个下午认真做一遍并且把这道题里每一个坑都填平我相信你会在真正走进笔试考场时比大多数人更从容。