领导,请停止对我的侵犯!
我在网上看到个吐槽帖,开头就很顶:“领导,请停止对我的侵犯。”点进去一看,原来是晚上10点突然拉人开复盘会。
有网友说得很直接:这哪是复盘,这是夜间突袭。还有人补刀:白天不开,非挑大家准备洗脸睡觉的时候,领导是把复盘当宵夜了。
我真觉得,员工反感的从来不是工作本身,是边界感被踩得嘎嘎响。复盘当然该做,成长也确实重要,可你总不能拿“负责”两个字,把别人的生活整个打包带走。成年人上班,是为了挣口饭吃,不是24小时待命当人形充电宝。
算法题:水壶问题
前几天刷题,看到一道老题:给两个容量分别为 x 和 y 的水壶,水无限,问能不能量出恰好 target 升水。
这题看着像小学奥数,真写起来,很多人会卡在两个地方: 一个是不知道怎么把“倒水”这件事抽象成程序;另一个是代码能跑,但自己也说不清为什么对。
先别急着上公式,我更喜欢先把它当成一个状态搜索题。
假设现在有两个壶,当前水量分别是 (a, b),那下一步能做的动作其实就这么几种:
# 1. 装满A
(a, b) -> (x, b)
# 2. 装满B
(a, b) -> (a, y)
# 3. 倒空A
(a, b) -> (0, b)
# 4. 倒空B
(a, b) -> (a, 0)
还有最关键的两步,互相倒水:
# A 倒给 B
move = min(a, y - b)
(a, b) -> (a - move, b + move)
# B 倒给 A
move = min(b, x - a)
(a, b) -> (a + move, b - move)
这样题目就清楚了: 从 (0, 0) 出发,看能不能走到某个状态,让 a == target,或者 b == target,或者 a + b == target。
这时候用 BFS 或 DFS 都能做。我平时更愿意写 BFS,状态一层层扩出去,不容易绕晕。
from collections import deque
defcan_measure_water(x: int, y: int, target: int) -> bool:
if target > x + y:
returnFalse
if target == 0:
returnTrue
q = deque([(0, 0)])
seen = {(0, 0)}
while q:
a, b = q.popleft()
if a == target or b == target or a + b == target:
returnTrue
nxt = []
nxt.append((x, b))
nxt.append((a, y))
nxt.append((0, b))
nxt.append((a, 0))
move = min(a, y - b)
nxt.append((a - move, b + move))
move = min(b, x - a)
nxt.append((a + move, b - move))
for state in nxt:
if state notin seen:
seen.add(state)
q.append(state)
returnFalse
比如 x = 3, y = 5, target = 4。 搜索路径里会出现这样一段:
(0, 0)
-> (0, 5)
-> (3, 2)
-> (0, 2)
-> (2, 0)
-> (2, 5)
-> (3, 4)
到 (3, 4) 的时候,第二个壶里正好是 4 升,答案就是 True。
不过这题还有个更像“面试官想听的答案”。
真正决定能不能凑出来的,不是搜索写得漂不漂亮,而是数学条件:target 必须不大于 x + y,并且 target 必须是 gcd(x, y) 的倍数。
代码能短很多:
import math
defcan_measure_water_math(x: int, y: int, target: int) -> bool:
if target > x + y:
returnFalse
if target == 0:
returnTrue
return target % math.gcd(x, y) == 0
这段代码短到有点不像题解,但它背后的意思很硬: 两个水壶反复装满、倒空、互倒,本质上只能拼出 gcd(x, y) 的倍数水量。
所以这题有两个解法思路:
一个适合你自己把过程想明白,拿状态搜索去推,代码也直观。 另一个适合面试里收尾,直接落到最大公约数。
真到面试现场,我一般会这么答:先说状态怎么建,再补一句这个题还能用 gcd 优化。这样既不像只会背结论,也不会显得只会暴力搜索。