年薪80万程序员,34岁考上了事业编,在教育局工作,每个月工资6000块。他爸妈现在逢人就夸自己儿子有出息,说比之前在大厂上班强多了
朋友原来在腾讯写代码,一年拿八十来万,放在打工人里已经算很能打了。结果34岁那年,他转头考回老家,进了教育局,一个月到手六千左右。
你以为家里会心疼收入掉这么多?没有。他爸妈现在见人就夸,儿子有本事,进单位了,端上铁饭碗了。以前在大厂赚钱,他们也高兴,但那种高兴像是“孩子会挣钱”。现在不一样了,像是祖坟冒青烟。
这事挺真实的。很多父母眼里,钱多不一定稳,听着也不一定体面。你说你在大厂年薪高,他们担心你哪天被裁;你说你在教育局,他们立马觉得这辈子踏实了。
程序员听完估计沉默,年薪80万干不过一个“有编制”,这职场价值观,真不是一套系统。
richer 这个入参挺容易把人带偏。
它不是让你去给所有人按财富排序,也不是让你补全一张“谁比谁有钱”的大表。题目真正要查的是:对每个人 x,在所有确定比他有钱的人里,再加上他自己,找一个最安静的人。
比如:
richer = [[1, 0], [2, 1], [3, 1]]
quiet = [5, 3, 6, 1]
[1, 0] 表示 1 比 0 有钱。
那查 0 的时候,不只看 1,还得继续往上看 2、3。因为 2、3 也可能通过关系链比 0 有钱。
这题我一般不会先想着排序。关系是单向的,而且每个人都要查一遍,如果每次都从头扫 richer,代码看着没毛病,跑大一点就开始发虚。
先把边反过来建。
原始关系是:
rich -> poor
但查询某个人时,我们要找“谁比他有钱”,所以更适合建成:
poor -> [rich1, rich2, ...]
代码可以这么写:
from typing import List
classSolution:
defloudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]:
n = len(quiet)
richer_map = [[] for _ in range(n)]
for rich, poor in richer:
richer_map[poor].append(rich)
ans = [-1] * n
defpick(person: int) -> int:
if ans[person] != -1:
return ans[person]
best = person
for rich_person in richer_map[person]:
candidate = pick(rich_person)
if quiet[candidate] < quiet[best]:
best = candidate
ans[person] = best
return best
for i in range(n):
pick(i)
return ans
这里有个细节,pick(person) 返回的不是安静值,而是人的编号。
这个地方我见过有人直接返回 quiet 值,前面跑着挺顺,最后发现题目要的是下标,又绕回去补映射。没必要,直接保存人就行。
再看一下递归里这几行:
best = person
for rich_person in richer_map[person]:
candidate = pick(rich_person)
if quiet[candidate] < quiet[best]:
best = candidate
意思很直白。
先假设自己最安静,然后去所有比自己有钱的人那里问一遍:你那条链上最安静的是谁?
如果对方链上找到的人更安静,就更新。
这题真正省时间的地方在 ans 这个缓存。
比如很多人都能追到同一个富人链,如果不缓存,每个人都会把那条链重新走一遍。缓存后,一个人的答案算过一次,后面直接拿。
这类题别急着写复杂结构。
先把问题方向掰正:我要从当前人往“更有钱的人”走。
再把重复计算掐掉:一个人的答案只算一次。
剩下就是 DFS。题目名字叫“喧闹和富有”,代码里其实没什么喧闹的地方,主要是别被 richer 那个方向骗了。