尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

网易2017秋招编程题全解析:四道真题拆解校招算法必考点

网易2017秋招编程题全解析:四道真题拆解校招算法必考点 如果你准备校招网易这套2017秋招编程题集合基本绕不开。我当年第一次刷的时候第一反应是“题目文字倒不长”结果一提交全红后来才发现坑全在细节里有前导零要处理有整除要判断还有一组数据没读完就急着输出。现在回头看这套题很适合两类人一类是准备参加互联网校招、想系统练算法题的同学另一类是单纯想通过真题了解大厂命题风格、顺便巩固基础的人。今天我就把这套题里最有代表性的几道完整过一遍从读题到建模再到代码落地全都拆开讲。1. 网易2017秋招编程题这套题到底在考什么1.1 考试形式与整体难度网易当年的在线笔试一般是牛客网或赛码网的环境每个岗位一套卷子编程题通常2到4道时间一般90到120分钟。题目不是那种需要“灵光一闪”才能想出来的脑筋急转弯更多是看你能不能把常见算法在有限时间内写对、写稳。我统计过这套题的大致特点第一道题往往是签到题考基本语法和简单逻辑只要不粗心就能过中间一到两道是模拟和数学推导核心是边界条件最后一道会拉高一点难度常驻选手是动态规划、贪心或搜索。整体难度在互联网大厂笔试里属于中等偏下但通过率并没有想象中高因为很多人不是不会做而是挂在输入输出和极端数据上。题型分布大致是这样的题目类型考察能力常见出题形式字符串处理代码基本功翻转、截断、去前导零数学逻辑推导能力解方程、整除判断动态规划建模能力最少步数、路径问题贪心局部最优判断棋盘放置、区间覆盖1.2 网易的命题风格与常见套路网易这套题有个很明显的特点主角永远叫“小易”。题目背景全是生活场景比如跳石板、放蛋糕、算糖果看着轻松实际上每一道都在考察你从文字里抽取出数学模型的能力。这种风格后来也延续到了网易游戏、网易云音乐这些产品的校招笔试里几乎成了网易技术岗的招牌。为什么要强调这个因为很多同学刷题只看算法忽略了“读题建模”这一关。实际笔试的时候题目不会直接告诉你“这题用DP”而是给你讲一个小故事你得自己判断出这是一个最短路、一个背包、还是一个解方程问题。网易这套题就是最好的建模训练素材题目不长干扰信息少很适合用来练“把叙述翻译成代码”的功夫。2. 四道必会真题的完整解题思路2.1 数字翻转别小看签到题这道题看起来最简单输入一个整数输出它的翻转结果。比如输入123输出321输入-123输出-321输入100输出1。但正因为简单很多人会踩到两个坑一个是前导零一个是负数。先说我见过最多的错误写法直接对字符串做reverse然后把反转结果转成数字最后再处理负号。这种思路不是不行但用字符串反转的时候很容易忽略一个点——题目给的是整数负数翻转的意思是把绝对值的数字翻转再恢复符号而不是把整个字符串反转成“321-”。所以最稳妥的做法是全程用整数运算。#include bits/stdc.h using namespace std; int main() { long long n; while (cin n) { long long num llabs(n); long long rev 0; while (num 0) { rev rev * 10 num % 10; num / 10; } if (n 0) rev -rev; cout rev endl; } return 0; }这里有两个细节值得展开。第一为什么用 long long因为 int 的最小值是 -2147483648如果你对它取绝对值int 会溢出结果还是负数后面翻转逻辑全乱。笔试环境里一定要养成“凡是可能取反的整数优先考虑 long long”的习惯。第二为什么写成 while (cin n)因为网易很多题目虽然描述里写着“输入一个整数”但后台测试数据其实有多组如果你只读一次样例能过提交就直接0分。所有在线笔试我默认都按多组输入处理这是血的教训。2.2 计算糖果把叙述翻译成方程这道题讲的是小易有一些糖果A、B、C三个人之间做减法加减法给你四个整数分别是 A-B、B-C、AB、BC让你反推出 A、B、C 的值。如果不存在这样的 A、B、C就输出 No。很多人一看到这种题就想枚举把 A 从0试到1000试完再试 B最后试 C。枚举不是不行但效率低而且容易漏边界。正确的做法是先看出这是个线性方程组设四个输入分别是 x1、x2、x3、x4x1 A - Bx2 B - Cx3 A Bx4 B C由 x1 和 x3 可以算出 A 和 BA (x1 x3) / 2B (x3 - x1) / 2由 x2 或 x4 算 CC (x4 - x2) / 2这道题真正的考点不是解方程而是“判定无解”。很多同学算出了 A、B、C 就高兴地输出了结果错了一半测试点。你可能心里想方程都列出来了怎么可能无解问题在于题目给的四个数不一定是合法方程组的输出。比如给负数、给奇数组合、或者四个数互相矛盾都可能出现无解情况。所以必须验证#include bits/stdc.h using namespace std; int main() { int x1, x2, x3, x4; cin x1 x2 x3 x4; int A (x1 x3) / 2; int B (x3 - x1) / 2; int C (x4 - x2) / 2; bool ok true; if ((x1 x3) % 2 ! 0) ok false; if ((x3 - x1) % 2 ! 0) ok false; if ((x4 - x2) % 2 ! 0) ok false; if (A 0 || B 0 || C 0) ok false; if (A - B ! x1) ok false; if (B - C ! x2) ok false; if (A B ! x3) ok false; if (B C ! x4) ok false; if (ok) cout A B C endl; else cout No endl; return 0; }我特别想强调最后那四个 if 验证。为什么算出来还要再验证一遍因为你用 x1 和 x3 算 A、B再用 x2 和 x4 算 C这中间可能存在“前两个方程满足、后两个方程不满足”的情况。比如输入 1 2 3 6解出来 A2、B1、C2但 BC3不等于 x46这种数据在后台测试里很常见。把验证写全这道题才算真正做完而不是“看起来做完了”。2.3 跳石板从DFS超时到动态规划这道题是整张卷子里比较有区分度的一道。题目大意是小易站在编号为 N 的石板上想跳到 M 号石板每次跳的步数必须是当前石板编号的约数而且不能是1和它本身。问最少跳几次能到 M到不了输出 -1。我见过不少同学的第一反应是DFS从 N 开始枚举每一个约数往下递归。理论上没问题但实际一跑就超时因为状态空间是指数级的。比如 N4约数是2跳到66的约数是2和3可以跳到8或98的约数是2和4跳到10或12……分支越来越多太多重复计算。正确的模型是最短步数问题而且状态之间有明显的方向性——每次跳跃都从 x 跳到 x d其中 d 1所以编号只会越来越大。这种“编号递增且求最少步数”的场景非常适合动态规划定义 dp[i] 表示从 N 跳到 i 号石板所需的最少次数初始 dp[N] 0其他地方设为一个足够大的数。然后从 N 开始往 M 遍历对于每一个可达的位置 i枚举 i 的所有约数 d尝试更新 dp[i d] min(dp[i d], dp[i] 1)。#include bits/stdc.h using namespace std; const int INF 0x3f3f3f3f; vectorint getFactors(int x) { vectorint res; for (int i 2; i * i x; i) { if (x % i 0) { res.push_back(i); if (i ! x / i) { res.push_back(x / i); } } } return res; } int main() { int N, M; cin N M; vectorint dp(M 1, INF); dp[N] 0; for (int i N; i M; i) { if (dp[i] INF) continue; vectorint factors getFactors(i); for (int d : factors) { if (i d M) { dp[i d] min(dp[i d], dp[i] 1); } } } cout (dp[M] INF ? -1 : dp[M]) endl; return 0; }这里有个关键优化枚举约数时只需要遍历到根号 x然后把成对的约数都加进列表。比如 x12遍历 i2 时得到约数2同时得到 12/26遍历到 i3 时得到3同时得到4。这样就不用从2一直试到 x-1时间复杂度从 O(Mx) 降到 O(M√M)数据范围到十万也能跑。还有个小细节约数列表里不包含1和自身是因为题目要求每次跳至少前进2格。这个条件保证了状态从左到右遍历是安全的不会出现回跳。如果你把约数理解成“所有因子”这题就废了。我当年就是没仔细读题把1算进去了结果dp数组原地更新输出了很多错误答案。2.4 不要二贪心标记与不可盲目套公式“不要二”这道题名字很有意思问题描述也很生活化一个 W×H 的网格每个格子里可以放一块蛋糕任意两块蛋糕之间的欧几里得距离不能等于2问最多能放多少块。第一步还是要建模。欧几里得距离等于2在整数坐标网格里只有两种情况同一行相差两列或者同一列相差两行。因为距离公式是 (x差)^2 (y差)^2 4满足条件的整数解只有 (2,0) 和 (0,2)不存在 (1,1) 这种组合因为112不等于4。这个结论很关键把几何问题直接转化成了行和列上的约束问题。接下来是放置策略。我推荐一种特别稳的贪心写法从左到右、从上到下遍历每一个格子如果当前格子没有被标记过就放一块蛋糕然后把它同一行向右两格的位置和同一列向下两格的位置标记为不可放。为什么不需要标记左边和上边因为如果左边或上边已经有蛋糕当前格子早就被标记过了根本不会走到这一步。这个写法直观、好记而且能处理所有 W 和 H 的组合。#include bits/stdc.h using namespace std; int main() { int W, H; cin W H; vectorvectorbool blocked(H, vectorbool(W, false)); int ans 0; for (int i 0; i H; i) { for (int j 0; j W; j) { if (!blocked[i][j]) { ans; if (j 2 W) blocked[i][j 2] true; if (i 2 H) blocked[i 2][j] true; } } } cout ans endl; return 0; }有几个细节容易搞错。首先是 W 和 H 谁是谁的问题。外层循环遍历行所以用 H 作为行数内层遍历列用 W 作为列数。如果你把 W 和 H 反了小数据可能不明显一旦测试用例给的是非对称的宽高结果就会错。其次是 i 和 j 的越界判断要分别用当前行和当前列去判断不要漏写任何一个。网上还有一种做法说每4行能放2行、每4列能放2列然后乘起来。我劝你不要背这个公式因为它依赖特定的放置顺序一旦理解偏差或者题目描述稍微变化很容易翻车。二维标记的贪心写法最多也就是 O(WH) 的复杂度对网易这套题的数据范围来说完全够用而且思路清清楚楚即使当场忘了“规律”也能靠逻辑推出来。3. 从题目到Offer考场上真正决定成败的细节3.1 输入输出与多组数据的坑我前面反复提到多组输入这里展开说一下。网易的在线笔试系统很多题目看起来只给了一组样例但后台评测数据可能是多组连续输入也可能是单组后跟EOF结束。最保险的写法就是在外层套一个 while(cin n) 或者 while(scanf(%d, n) ! EOF) 的循环把整个逻辑包进去。C 还有一个性能细节如果你用 cin/cout建议在 main 开头加两句ios::sync_with_stdio(false); cin.tie(0);这两行能显著减少 IO 时间。笔试时一旦数据量到十万级别不加这行和加这行的差距就是“险过”和“稳过”的区别。不过要注意关闭同步之后不要再混用 printf 和 cout否则输出顺序会乱。如果你是Java选手优先用 BufferedReader 和 StringTokenizer别用 Scanner。Scanner 在读写几万行数据的时候会明显变慢我见过不少同学算法写对了结果因为输入解析太慢被卡超时非常冤。3.2 边界条件自测清单写完代码之后别急着提交先在心里跑一遍边界数据。特别是这几类边界类型例子容易错的地方最小值N0、负数取绝对值溢出、数组越界最小值N1循环根本进不去输出初值相等N M不需要跳步数应该是0无解到不了 M输出 -1最大值接近 int 上限加法溢出要用 long long单行/单列W1 或 H1二维数组的边界判断拿跳石板举例N 和 M 相等的时候dp[M] 已经被初始化成0你的代码要能直接输出0。拿数字翻转举例输入是0的时候循环 while(num 0) 一次都不执行rev保持0输出0这是对的。但如果你用 do-while 写就会多翻转出0看起来没区别但如果题目要求去前导零逻辑就可能出问题。3.3 时间分配先保底再冲高网易这套题虽然总量不多但实际笔试时你会紧张时间会被反复调试吃掉。我的建议是前两道题尽量在30分钟内解决剩下时间留给动态规划这类题目。如果遇到一道题暂时没思路不要死磕先写一个暴力版本哪怕只能过30%的测试点也比空着强。在线笔试的判分通常按测试点给分部分通过也是分。我见过太多人从头到尾只做一道题最后其他题全空白连最基本的签到分都没拿到。正确策略是先把所有题都读一遍把会做的做掉再做半懂不懂的最后有时间再回来处理完全不会的。很多同学还会犯一个错误改完一个 bug 就直接提交没有把改动的代码完整看一遍。有时候你为了修一个越界不小心把输出语句挪到了循环外面这种低级错误完全可以通过“提交前检查一遍主流程”避免。4. 我踩过的坑与问题排查实录4.1 样例过了提交却0分这是校招笔试里最崩溃的情况没有之一。我自己当年也经历过后来复盘下来原因就那几个第一输出格式不一致。题目要求输出 No你输出了 NO 或者 no题目要求数字之间用空格分隔你多打了换行或者行尾多了个空格。OJ 对输出的判空非常严格多一个空格都可能判错。这种问题样例往往发现不了因为样例不会把每个字符都标出来。第二多组输入没有处理。你只读了一组数据然后直接 return后面几组数据全被忽略自然0分。我在前文已经给了标准写法这里再强调一次所有输入默认按多组处理直到 EOF。第三数组开小了。比如跳石板你看到 M 最大是 100000就把 dp 开成 100000但访问了 dp[M] 或 dp[id]如果 id 恰好等于 100001就越界了。最稳妥的做法是开 M5 甚至 M10多出来的空间反正不用也不影响复杂度。第四int 溢出。计算糖果里 A (x1 x3) / 2如果 x1 和 x3 都接近 int 上限相加会直接溢出成负数然后判断 A 0输出 No但正确答案其实存在。这类问题很难通过肉眼发现所以凡是涉及加法运算的中间结果优先用 long long 或者 long。4.2 DFS爆栈或递归超时以跳石板为例这道题很多人都卡在DFS上。我见过有同学写的递归函数估算出结果但一提交就超时或栈溢出。原因有两个一是分支太多指数级增长二是即使加了记忆化每次递归都要维护一个 visited 数组空间开销也不小。用动态规划替代递归最大的好处是每个状态只计算一次顺序从前往后推不需要栈帧。如果你在考场上发现递归超时可以先想想状态能不能压缩成一维数组能不能从左到右递推。答案是能的话就果断改成DP。跳石板的递推方向是确定的因为步数 d 一定大于0所以从 N 到 M 正着推就行不需要担心环形依赖。4.3 网易笔试高频注意事项速查表最后整理一张速查表建议在笔试前翻一眼问题现象可能原因排查方式样例能过但0分输出多了空格或换行逐字节检查输出第一组样例过了后面全错没有循环读入多组输入使用 while(cin n)数组越界或崩溃数组大小不够开到上限 5数据量大时超时cin/cout 未优化关闭流同步结果始终是No/失败中间结果溢出换成 long longDP结果差1初始状态没设置对检查 dp[N] 是否为0读入顺序错了没有按题目要求读 W、H打印输入参数核对说实话网易这套2017秋招编程题集合现在拿出来看依然不过时。我在实际带人的过程中发现能把这几道题完全吃透的人去应付大部分互联网公司的在线笔试都很有底气。因为本质考察的从来不是题目本身而是你遇到一个陌生描述时能不能快速定位到熟悉的算法模型并且把边界条件抠清楚。如果你有时间还可以把2018年、2019年的网易笔试题也翻出来对比看看你会发现命题风格是一脉相承的很多题其实就是换了个故事背景。最后祝准备笔试的同学都能顺利通关别像我当年那样样例跑得飞快一提交就红成一片。
返回列表