在大厂,一位年薪300W的P9告诉我,领导最烦这种下属,哪怕技术再牛逼,也不会给晋升机会。。
刚看到个贴子,说在大厂,一个年薪300万的P9分享:领导最烦那种只顾技术、不懂汇报、不和人打交道的下属。哪怕技术再强,也升不上去。
我觉得这事挺现实的。网友们都说“技术牛逼还不够”,其实真的是。公司不是技术竞赛,是资源博弈场。你能不能让领导省心、让团队顺畅,往往比你代码写得多快更重要。别以为会点AI、搞点架构就能封神,职场升迁看的从来不是“最能干的”,而是“最有用的”。
当然,有人反感这种“拍马屁文化”,觉得不公平。但换个角度看,职场就是组织协作,领导不信任你、不懂你的价值,怎么敢把位子交给你?
说到底,技术是敲门砖,关系是门把手。【备注:文末可领最新资料】
算法题:完全子集的最大元素和
这个题叫「完全子集的最大元素和」,先把题意用大白话说一下:
有个数组 nums,下标从 1 开始,到 n 结束。你要选出一批下标,比如 {i₁, i₂, …, ik},要求这批下标里任意两两相乘,结果都是完全平方数(比如 1,4,9,16… 这种)。这样的下标集合叫「完全集」。题目让我们在所有完全集里,选一个,让对应的元素和 nums[i₁] + ... + nums[ik] 最大。
因为 nums[i] 都是正整数,所以直觉上:只要一堆下标能一起组成一个完全集,那就把这一堆里所有下标都拿上,和肯定比只拿其中一部分更大。
怎么理解「i * j 是完全平方数」?
关键条件是:对完全集里任意两个下标 i 和 j,都有 i * j 是完全平方数。
完全平方数有个经典结论: 把一个数做质因数分解,x = p1^a1 * p2^a2 * ...它是完全平方数,当且仅当每个指数 a1, a2, ... 都是偶数。
那 i * j 是完全平方数,就说明:i 和 j 分解后,把每个质因数的指数加起来,结果都是偶数。
这意味着什么? 对每个质因数 p,i 里面 p 的指数和 j 里面 p 的指数,奇偶性必须相同:
要么俩都是奇数 要么俩都是偶数
所以我们完全可以只关心「每个质因数指数的奇偶性(模 2)」,不用管具体是几次方。
把下标「压扁」成一个标识
更进一步,可以给每个下标 i 贴一个「标签」:
把 i做质因数分解。对每个质数,指数是偶数就当 0,是奇数就当 1。 把所有「指数是奇数」的质因数乘起来,得到一个数,记为 core(i)。
这个 core(i) 就是 i 去掉所有平方因子后的「平方自由部分」(有时候也叫「平方因子核」)。比如:
i = 12 = 2^2 * 3^1,指数奇偶:2(偶),3(奇),所以core(12) = 3i = 18 = 2^1 * 3^2,指数奇偶:2(奇),3(偶),所以core(18) = 2i = 36 = 2^2 * 3^2,全是偶数,core(36) = 1
现在看 i * j 的质因数指数,就是把它们的奇偶向量相加(模 2),要变成全 0,只有一种情况: 两人的奇偶向量一模一样。
翻译一下就是:
对于两个下标
i和j,i * j是完全平方数 ⇔core(i) == core(j)。
那题目的限制就非常清晰了:
一堆下标能组成一个完全集,当且仅当这堆下标的 core(i) 全都相同。
所以所有完全集就是:
把下标 1..n按core(i)分组每一组内部随便怎么选,下标们彼此都能组成完全集 全是正数,那当然是「这个组全要」最好
于是问题就变成了:
按
core(i)把下标分组,每组把对应的nums[i]全加起来,最后取一个最大的组和。
算法步骤梳理一下
整体思路其实很简单,就是「分组 + 求和」:
用一个线性筛预处理
1..n每个数的最小质因数(spf),方便后面快速分解下标。对每个下标
i(注意是下标,不是nums[i]):
用 spf 做质因数分解,统计每个质因数指数的奇偶性; 把所有指数为奇数的质因数乘起来,当成这个下标的 core(i)。
用一个 HashMap<Integer, Long>,键是 core(i),值是这一组的元素和。
遍历下标 i,对core(i)对应的那一组,加上nums[i - 1](Java 里数组是 0 下标)。
再扫一遍 map,找到最大的那一组的和,就是答案。
时间复杂度大概是:
筛最小质因数: O(n log log n)每个下标用 spf 分解:均摊 O(log n),总共O(n log n)在 n ≤ 1e4 的限制下完全够用。
Java 实现代码
直接给一个比较完整、还能直接丢到 LeetCode 上跑的写法:
import java.util.*;
publicclassSolution{
publiclongmaximumSum(int[] nums){
int n = nums.length;
// 下标从 1..n,用到 n 即可
int[] spf = buildSpf(n); // smallest prime factor
Map<Integer, Long> groupSum = new HashMap<>();
for (int i = 1; i <= n; i++) {
int core = getCore(i, spf);
long old = groupSum.getOrDefault(core, 0L);
groupSum.put(core, old + nums[i - 1]);
}
long ans = 0;
for (long v : groupSum.values()) {
if (v > ans) ans = v;
}
return ans;
}
// 线性或类线性筛最小质因数,这里简单一点写
privateint[] buildSpf(int n) {
int[] spf = newint[n + 1];
for (int i = 2; i <= n; i++) {
if (spf[i] == 0) { // i 是质数
spf[i] = i;
if ((long) i * i <= n) {
for (int j = i * i; j <= n; j += i) {
if (spf[j] == 0) {
spf[j] = i;
}
}
}
}
}
// 可能有少量合数没被标到(比如 2 * 质数 > n 时),补一下
for (int i = 2; i <= n; i++) {
if (spf[i] == 0) spf[i] = i;
}
return spf;
}
// 计算下标 i 的 square-free 核心 core(i)
privateintgetCore(int x, int[] spf){
int core = 1;
while (x > 1) {
int p = spf[x];
int cnt = 0;
while (x % p == 0) {
x /= p;
cnt ^= 1; // 只关心奇偶
}
if (cnt == 1) {
core *= p;
}
}
return core;
}
// 简单测一下
publicstaticvoidmain(String[] args){
Solution s = new Solution();
int[] nums1 = {8,7,3,5,7,2,4,9};
System.out.println(s.maximumSum(nums1)); // 16
int[] nums2 = {5,10,3,10,1,13,7,9,4};
System.out.println(s.maximumSum(nums2)); // 19
}
}
整套下来,其实就是一句话:
把「两两下标乘积是完全平方数」翻译成「下标按平方自由核分组」,然后在每组里把
nums求和取最大。
数学味儿稍微有一点,但搞清楚「只看质因数指数奇偶性」这件事之后,代码其实挺顺的。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html