这两年找工作真太难啦。我做hr的朋友告诉我,每天能收到上百份简历,一个3.5k的薪资岗位。
很多人还以为是自己太菜,结果一看,周围一圈都这样。不是你一个人石沉大海,是整个池子都快塞满了。
有网友说,现在不少岗位挂着像在招人,实际是在攒简历,顺便做做市场价摸底。
说白了,现在不是你认真就一定有回应,很多时候是岗位少、投的人多,企业又挑得离谱,三千块的活想招个会七种技能还自带情绪稳定的。HR看完简历没回,不一定是你不行,也可能是这岗自己就挺抽象。你要说行情差不差,差到大家开始怀疑人生,这就已经很说明问题了。
这题表面上像二分,真到面试里,很多人会卡在“为什么能二分”。不是数组,不是下标,是对“答案”二分。你先别急着写递归,先盯住题目里的那个词:最大值最小。这种味道,一般就不是在枚举切法了,而是在猜一个上限,看这个上限能不能把数组切成不超过 m 段。
比如数组是 [7,2,5,10,8],分成 2 段。你猜最大子数组和上限是 15,那就从左往右累加,超过 15 就必须切一刀。最后发现需要切成 3 段,说明这个上限太小,压不住。反过来,如果上限是 18,能在 2 段内搞定,那这个值就有机会继续往小里收。
这题我第一眼就不太信 DP。不是不能做,是写出来又长又重,面试里很容易把自己写进沟里。二分 + 贪心更像这题该有的解法。
先看核心判断逻辑,代码不用长:
privatebooleancanSplit(int[] nums, int m, long limit){int count = 1;long sum = 0;for (int num : nums) {if (sum + num > limit) { count++; sum = num;if (count > m) {returnfalse; } } else { sum += num; } }returntrue;}
这里有个细节挺容易写错:count 从 1 开始,不是 0。因为哪怕一刀不切,也已经有一段了。
完整解法如下:
publicclassSolution{publicintsplitArray(int[] nums, int m){long left = 0, right = 0;for (int num : nums) { left = Math.max(left, num); // 下界:单个元素最大值 right += num; // 上界:整个数组和 }while (left < right) {long mid = left + (right - left) / 2;if (canSplit(nums, m, mid)) { right = mid; } else { left = mid + 1; } }return (int) left; }privatebooleancanSplit(int[] nums, int m, long limit){int count = 1;long sum = 0;for (int num : nums) {if (sum + num > limit) { count++; sum = num;if (count > m) {returnfalse; } } else { sum += num; } }returntrue; }}
这题真正要讲明白的,不是代码,是两个边界。
下界一定是数组里的最大值。因为你怎么切,都不可能把 10 这种元素切开,所以答案绝不会小于它。上界就是整个数组和,实在不切,最大值就是总和。
再一个就是贪心为什么成立。因为我们验证某个 limit 时,目标不是找最优切法,而是判断“能不能切出来”。这时候每一段尽量往后吞,超过了再切,是最省段数的做法。省段数都做不到 m 以内,那别的切法更没戏。
时间复杂度是 O(n * log(sum)),比起 O(n^2 * m) 的 DP,面试里这个更稳,也更像真正做题时会选的方案。
这题有意思的地方就在这儿:看着像分组,实际上是在猜答案。题目一旦出现“最大值最小”“最小值最大”,我一般先想二分,不急着上状态转移。这个习惯挺值钱。