在职4年多,归还电脑时,因为有4个角磕碰,要收我 275块钱。真真恶心我呀。
刚看到个贴子,说有人在职4年多,交还公司电脑,因为四个角有磕碰,被要求赔275块,气得直说“真恶心”。
网友大概两派:一派骂公司抠门,小磨损也要算钱;一派说入职时都签资产单了,按制度办事没毛病。
我觉得这事恶心的点,不在钱,在那种“被当成外人清账”的感觉。干了几年,最后一刻只剩一张赔偿单,谁都会心凉。但话说回来,公家东西总要有人负责,摔了磕了,总不会自己长好。
关键还是真正的规矩和沟通:损坏怎么界定、折旧怎么算,提前说清楚;事后态度客气一点,这275块就是流程;要是啥都不说直接扣,那确实寒心。
面试题:变为棋盘
昨晚十一点多吧,我在公司楼下抽烟那个地儿,风贼大,手机还一直震…我们组小李在群里甩了个算法题,说“哥,变为棋盘这题我看麻了”,我当时脑子里还在想线上那个缓存穿透咋整,结果一看题名,哦这不就是那种“0/1矩阵,靠交换行列变成棋盘格”的老六题么。
你们知道吧,这题最坑的不是写交换,是你得先证明“能不能变”。我一般不从“怎么换”开始讲,我先盯着矩阵的“基因”,就是第一行、第一列。因为如果能变成棋盘,那每一行要么跟第一行一模一样,要么跟第一行完全相反(0变1、1变0那种)。列也是一样的道理。要是你扫一遍发现有一行既不是原版也不是反版,那就别演了,直接 -1,换到天荒地老也不行。
然后是“最少交换次数”这个事。交换行列本质上就是把行的顺序、列的顺序重排,让它变成 0101… 或 1010… 两种交错。这里有个小细节我以前也栽过:n 是奇数的时候,棋盘里 1 的数量只能是 ⌈n/2⌉ 或 ⌊n/2⌋,所以你不能两个模式都选,得选那个“1的个数对得上”的模式,不然算出来交换次数看着很美,实际是假的。
我当时在楼下冻得手都不利索了,就用 bitmask 来压一行(Java里用 long 稳点),一边哈气一边敲,核心代码差不多这样(你直接拿去跑就行):
publicclassChessboardTransform{
publicintmovesToChessboard(int[][] board){
int n = board.length;
long rowMask = 0, colMask = 0;
for (int i = 0; i < n; i++) {
rowMask = (rowMask << 1) | board[0][i];
colMask = (colMask << 1) | board[i][0];
}
long rowInv = invert(rowMask, n);
long colInv = invert(colMask, n);
int rowCount = 0, colCount = 0;
for (int i = 0; i < n; i++) {
long r = 0, c = 0;
for (int j = 0; j < n; j++) {
r = (r << 1) | board[i][j];
c = (c << 1) | board[j][i];
}
if (r != rowMask && r != rowInv) return -1;
if (c != colMask && c != colInv) return -1;
if (r == rowMask) rowCount++;
if (c == colMask) colCount++;
}
// 行/列模式出现次数也得合法:只能是 n/2 或 n/2+1
if (!validCount(rowCount, n) || !validCount(colCount, n)) return -1;
int rowSwaps = minSwapsToAlternate(rowMask, n);
int colSwaps = minSwapsToAlternate(colMask, n);
if (rowSwaps < 0 || colSwaps < 0) return -1;
return rowSwaps + colSwaps;
}
privatebooleanvalidCount(int cnt, int n){
return cnt == n / 2 || cnt == (n + 1) / 2;
}
privatelonginvert(long mask, int n){
long all = (n == 64) ? -1L : ((1L << n) - 1);
return (~mask) & all;
}
privateintminSwapsToAlternate(long mask, int n){
long patternA = buildPattern(n, 0); // 0101...
long patternB = buildPattern(n, 1); // 1010...
int ones = Long.bitCount(mask);
if ((n & 1) == 0) {
int diffA = Long.bitCount(mask ^ patternA);
int diffB = Long.bitCount(mask ^ patternB);
return Math.min(diffA, diffB) / 2;
} else {
// 奇数:1的数量必须固定,只有一个pattern可用
int onesA = Long.bitCount(patternA);
int onesB = Long.bitCount(patternB);
if (ones == onesA) return Long.bitCount(mask ^ patternA) / 2;
if (ones == onesB) return Long.bitCount(mask ^ patternB) / 2;
return -1;
}
}
privatelongbuildPattern(int n, int startBit){
long p = 0;
int bit = startBit;
for (int i = 0; i < n; i++) {
p = (p << 1) | bit;
bit ^= 1;
}
return p;
}
}
你看我这个写法就很“工程味”,先验尸:行列是不是“同源/反源”,再算手术方案:行要换几次、列要换几次。为啥除以2?因为 bitCount 算出来是“有多少位置不一样”,一次交换能修正两个位置的错位,嗯差不多就这意思,你别跟我抬杠哈,抬杠我就让小李去写证明题了。
哦对了,还有个很像线上排查的感觉:你以为问题出在“交换策略”,结果大部分 case 死在“根本不可能变”。就像你们线上看着慢,结果是依赖服务超时,业务代码还在傻跑…咦我怎么又扯到线上了,算了我先上去开会了,电梯来了。