Python技术迷

昨天晚上7点多,部门主管突然微信跟我说,有一笔1500元的团队激励奖,人事文员已经直接填了我的银行卡号

昨天晚上7点多,我接到了部门主管的微信,告诉我有一笔1500元的团队激励奖需要发放。

主管说人事文员已经把我的银行卡号填上了,并会将这笔奖金和我的工资一起发到我的卡上。主管的要求是,收到这笔款项后,我要把钱转给人事文员,由她再分配给部门其他同事。

Image

这笔奖金是团队的激励,但为何由我来转账给其他人?这是否是一个正常的流程,或者是否存在一些问题。

虽然告诉我奖金的发放方式可能没问题,但确保整个过程的透明和公平依然非常重要。

算法题:最佳的碰头地点

一开始很多人会把这题写成“枚举每个空地,算所有人走过去的总距离”。代码不难,提交也不一定马上超时,但这思路我第一眼就不太信。网格一大,m * n 个位置都试一遍,再把所有人的坐标重新扫一轮,味道就不对了。

这题叫“最佳的碰头地点”,本质不是找一个格子,而是找一个让曼哈顿距离和最小的位置。公式长这样:

abs(x1 - x) + abs(y1 - y)

看到这个式子,先别急着碰二维。老写线上问题的人都知道,二维拆一维,往往就顺了。

因为总距离其实可以拆开:

sum(abs(xi - x)) + sum(abs(yi - y))

也就是说,横坐标和纵坐标可以分别处理。那问题就变成了:

  • 一堆横坐标,选哪个点,距离和最小?
  • 一堆纵坐标,选哪个点,距离和最小?

这地方经验值就出来了:一维上让绝对值距离和最小的点,是中位数。

不是平均数。很多人下意识会想到平均数,但平均数更适合平方距离,绝对值距离不是这一套。比如点在 1, 2, 100,平均数是 34,显然很离谱;中位数是 2,才对。

所以这题其实没那么玄:

  1. 扫一遍网格,把所有人的行号收集出来。
  2. 再把所有人的列号收集出来。
  3. 行坐标取中位数,列坐标取中位数。
  4. 分别累加距离。

代码我自己一般会写成这样,不花哨,但够稳:

from typing import List

classSolution:
defminTotalDistance(self, grid: List[List[int]]) -> int:
        rows = []
        cols = []

        m, n = len(grid), len(grid[0])

for i in range(m):
for j in range(n):
if grid[i][j] == 1:
                    rows.append(i)

for j in range(n):
for i in range(m):
if grid[i][j] == 1:
                    cols.append(j)

        row_mid = rows[len(rows) // 2]
        col_mid = cols[len(cols) // 2]

        ans = 0
for r in rows:
            ans += abs(r - row_mid)
for c in cols:
            ans += abs(c - col_mid)

return ans

这里有个小细节,rows 不需要排序,因为按行扫描天然有序;cols 我是按列扫描,也天然有序。这样连 sort() 都省了。

比如这组数据:

grid = [
    [1, 0, 0, 0, 1],
    [0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0]
]

人的位置是 (0,0)、(0,4)、(2,2)。

  • 行坐标是 [0, 0, 2],中位数是 0
  • 列坐标是 [0, 2, 4],中位数是 2

碰头点取 (0,2),总距离就是:

abs(0-0)+abs(0-2) +
abs(0-0)+abs(4-2) +
abs(2-0)+abs(2-2) = 6

这题真正值钱的地方,不是把代码写出来,而是能不能看出来:二维曼哈顿距离,拆成两个一维中位数问题。

很多算法题表面是网格,实际考的是你会不会降维。你要是一直盯着每个格子去试,最后就很容易写成一个能跑、但不漂亮的暴力解。

这题收住就一句:别在二维里死扛,中位数一出来,题就差不多结束了。