和同事嬉戏打闹,一不小心亲了他一口,现在应该怎么收场?
刚看到个贴子:网友说和同事打闹时没收住,居然亲上去一下,现在尴尬到脚趾抠出三室两厅,不知道该怎么收场。
我觉得这事吧,说大不大,说小也不小。关键在于——你们本来就有点暧昧,还是纯纯的同事关系?
网友的回帖我也看了,有人说赶紧装作没发生,有人劝表白,有人建议请假躲两天……但我个人更倾向于冷静处理,别把职场关系搅成偶像剧。
如果这位同事对你也有意思,那就坦诚一句“刚刚有点过头了,不好意思”,看对方反应,再决定要不要顺势把关系往前迈一步。
如果对方明显慌了或者不想多聊,那就赶紧把氛围拉回工作区,轻描淡写过去,装糊涂反而是最体面的解决方式。
玩闹归玩闹,踩线就要负责善后。别逃避,也别矫情,轻松处理,给彼此台阶。【备注:文末可领最新资料】
面试题:栅栏涂色
先说个小场景哈: 前两天我们组小李在那儿刷题,突然跑过来问我:“东哥,一个栅栏,很多根柱子,要刷成五颜六色,咋算一共有多少种刷法啊?” 我一听这味儿,就知道是典型的算法面试题——栅栏涂色。
一般这题长这样(最常见那版):
有 n 根栅栏,每根栅栏都要刷颜色;一共有 k 种颜色可以选。 要求是:不能有 3 根连续的栅栏颜色一样(也就是最多只能有 2 根挨在一起同色)。 问:一共有多少种不同的涂色方案?
比如:
n = 1,不管多少颜色,随便选,答案就是 k。 n = 2,第一个随便选 k 种,第二个要么跟第一个一样,要么不一样,都可以,所以是 k * k。
但从第 3 根开始,就得小心不能“连着三根同色”。
最直接的想法就是: 把每一根栅栏都当成一个位置,第 i 根随便挑一个颜色,只要不违反规则就行。 理论上你可以用 DFS / 回溯,从第 1 根到第 n 根,一根一根试颜色:
对每个位置枚举 k 种颜色 如果跟前两根冲突(出现 3 连),就剪枝 最后数一数有多少种合法方案
听起来挺直观,但复杂度是啥?最坏情况大概 O(k^n),n 稍微大一点,直接原地去世,面试官都要给你递纸巾了。所以这个思路适合理解,不适合真的写在答案里。
这时候就得上动态规划了。
动态规划,关键是“怎么记状态”
这种“当前位置、跟前面有啥关系”的问题,很适合 DP。 我们想一想:第 i 根栅栏,真正跟它有关系的,是前面两根:
如果第 i 根和第 i-1 根颜色一样,那就要求第 i-1 根必须和第 i-2 根不一样(否则就三连了)。 如果第 i 根和第 i-1 根不一样,那前面爱咋样咋样,反正不可能因为我这根导致 3 连。
所以我们可以按“第 i 根这一下的状态”来分类:
same[i]:第 i 根和第 i-1 根颜色相同的方案数 diff[i]:第 i 根和第 i-1 根颜色不同的方案数
那总方案数就是:same[i] + diff[i]。
接下来就看怎么推到下一根。
1)算 same[i] 想让第 i 根和第 i-1 根一样,只能从哪种情况转移过来? 必须是:在第 i-1 根时,它和前一根“不同色”的那些方案,因为如果 i-1 和 i-2 已经相同了,再让 i 和 i-1 也相同,那就三连了。
所以: same[i] = diff[i - 1]
2)算 diff[i] 想让第 i 根和第 i-1 根不同颜色: 只要先不管前面是什么样(same[i-1] + diff[i-1]),然后给第 i 根选一个“和第 i-1 根不同的颜色”就行。 如果总共有 k 种颜色,而 i-1 已经用了 1 种,那我这次有 (k - 1) 种选择。
所以: diff[i] = (same[i - 1] + diff[i - 1]) * (k - 1)
这个递推关系一出来,基本就稳了。
再补一下初始值:
如果 n = 1:
same[1] = 0(只有一根,没办法和前一根“相同”) diff[1] = k(随便选一种颜色) 答案 = k 如果 n ≥ 2,可以这样想:
same[2]:两根同色,第一根有 k 种,第二根只能跟它一样,所以 same[2] = k diff[2]:两根不同色,第一根 k 种,第二根 (k - 1) 种,所以 diff[2] = k * (k - 1) 对第 2 根来说:
当然你也可以从 n=1 的状态直接往后推,一样能跑出来。
时间复杂度 O(n),空间用两个变量就够了,完全可以拿去写答案。
Java 实现,尽量写得清爽一点
按上面的思路,用 Java 写个方法,大概就是这样:
publicclassPaintFence{
// n: 栅栏数量
// k: 颜色数量
publicintnumWays(int n, int k){
// 特殊情况处理
if (n == 0 || k == 0) {
return0;
}
if (n == 1) {
return k;
}
// same 表示当前这一根和前一根颜色相同的方案数
// diff 表示当前这一根和前一根颜色不同的方案数
long same = k; // 对第 2 根来说,两根同色的方案
long diff = (long) k * (k - 1); // 对第 2 根来说,两根不同色的方案
// 从第 3 根开始往后推
for (int i = 3; i <= n; i++) {
long newSame = diff; // same[i] = diff[i - 1]
long newDiff = (same + diff) * (k - 1); // diff[i] = (same[i-1] + diff[i-1]) * (k - 1)
same = newSame;
diff = newDiff;
}
long result = same + diff;
// 这里题目一般会让你取模的话再 % MOD,这里就直接转 int。
return (int) result;
}
// 简单测一下
publicstaticvoidmain(String[] args){
PaintFence pf = new PaintFence();
System.out.println(pf.numWays(1, 3)); // 3
System.out.println(pf.numWays(2, 3)); // 9
System.out.println(pf.numWays(3, 2)); // 可以自己手算对比一下
}
}
这里我用 long 是为了中间计算别轻易溢出,最后如果题目要求取模,比如 1e9+7,你就在 result 计算完之后加一句 % MOD 就行。
顺带提一句:如果只是“相邻不能同色”
有些题叫栅栏涂色,但条件更简单:只要求相邻两根不能同色,没有“三连”这个限制,那就更好算了:
第一根 k 种 每一根都只能从剩下的 k-1 种选 总数就是:k * (k - 1)^(n - 1)
这个就不展开了,面试的时候一定要先看清题目,是“不能 3 连”还是“相邻不同”,别一上来就写错版本。
大概就这样,你可以先按这个版本敲一遍,把递推打印出来看看 same / diff 的变化,会更直观一点。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html