程序员老鬼

38岁的总监,某大厂P9,被裁了。现在在卖保险!

我在网上看到一个帖子,味儿一下就上来了:38岁的总监,某大厂P9,去年被裁,转头去卖保险了,而且第一个月就冲进了MDRT。。

Image

网友们看完也挺热闹。有人说,大厂P9失业后去卖保险,这哪是转行,分明是把“向上管理”和“资源整合”切到新地图了。还有人调侃,普通人被裁了先怀疑人生,P9被裁了先怀疑哪家客户还没薅到。话糙理不糙,职位做到这个级别,最值钱的还真不一定是那张工牌,而是脑子、圈子和那套见人说人话见项目讲人话的本事。

大厂身份会过期,能搞定人的本事,反倒更像硬通货。

面试题:轰炸敌人

这道“轰炸敌人”我第一次看到时,直觉是暴力枚举:把每个空位 0 都试一遍,向上、下、左、右扫过去,遇到墙 W 停,统计能炸到多少个敌人 E。写出来不难,但一旦网格大一点,性能就不太好,空位一多,很多行列会被重复扫描。

这个题真正该盯住的地方,不是“怎么炸”,而是“同一段没有墙隔开的行和列,其实可以复用统计结果”。

比如这一行:

0 E 0 0 W E E 0

在 W 左边这一段里,不管你站在哪个 0 上,横向能炸到的敌人数其实是一样的。列也是同理。那就没必要每个空位都重新扫四个方向了。

我一般会这么写: 遍历网格时,维护两个量:

  • rowHits:当前格子所在这一段横向能打到多少敌人
  • colHits[j]:第 j 列当前这一段纵向能打到多少敌人

只有在“这一段刚开始”的时候才重新统计。也就是:

  • 当前列是 0,或者左边是墙,重算 rowHits
  • 当前行是 0,或者上边是墙,重算 colHits[j]

核心代码我给你贴完整一点,Java 写法如下:

classSolution{
publicintmaxKilledEnemies(char[][] grid){
if (grid == null || grid.length == 0 || grid[0].length == 0) {
return0;
        }

int m = grid.length;
int n = grid[0].length;
int[] colHits = newint[n];
int ans = 0;

for (int i = 0; i < m; i++) {
int rowHits = 0;
for (int j = 0; j < n; j++) {

if (j == 0 || grid[i][j - 1] == 'W') {
                    rowHits = 0;
for (int k = j; k < n && grid[i][k] != 'W'; k++) {
if (grid[i][k] == 'E') {
                            rowHits++;
                        }
                    }
                }

if (i == 0 || grid[i - 1][j] == 'W') {
                    colHits[j] = 0;
for (int k = i; k < m && grid[k][j] != 'W'; k++) {
if (grid[k][j] == 'E') {
                            colHits[j]++;
                        }
                    }
                }

if (grid[i][j] == '0') {
                    ans = Math.max(ans, rowHits + colHits[j]);
                }
            }
        }

return ans;
    }
}

这个写法有个挺实在的点:虽然看起来里面还有两层 for,但不是每个位置都把整行整列扫一遍。每一段连续区域只会被统计一次,所以整体复杂度能压到 O(m * n),空间复杂度是 O(n)。

这题还有个容易写错的地方:只能把炸弹放在 0 上,不能放在敌人 E 上;另外,炸弹传播时遇到 W 就必须停,墙后面的敌人不算。这两个条件如果漏一个,结果基本就偏了。

拿样例看一下:

char[][] grid = {
    {'0','E','0','0'},
    {'E','0','W','E'},
    {'0','E','0','0'}
};

中间那个 grid[1][1] 就是最优点,横着能炸左边一个,竖着能炸上边和下边各一个,一共 3 个。

这种题不算特别难,但挺适合练“重复计算怎么消掉”。面试里如果你一上来就是四方向暴力,其实也还能聊;但再往下走一步,把“按墙分段复用统计值”说出来,代码味道就不一样了。