细思极恐!提了离职后公司电脑的灯一直亮着,都要走了,还要被时刻监控~
刚看到个贴子,说有大厂员工提完离职后,公司电脑摄像头的灯一直亮着。嗯…这事确实有点背脊发凉的感觉,谁都不想走都要走了还被盯着看。
这事关键不在“灯亮”,而在“边界感”。网友们有人说可能是系统 bug,也有人说大厂监控本来就严格。我看了看,两个说法都不算离谱,但员工的感受不能被忽略。
毕竟电脑就像你的工位延伸,离职后还亮着就像你已经关门了,结果有人从猫眼里一直往里瞄,能不别扭吗?
换个角度想,公司确实有信息安全压力,但安全和尊重之间还是要有条线。
你要查资料、锁权限都没问题,但别让员工觉得“你走了我还在盯着你的一举一动”。这种气氛久了,只会让大家更心寒。
制度可以严,边界要清晰。职场再卷,也别把最基本的尊重卷没了。【备注:文末可领最新资料】
面试题:大礼包
先说结论:这题“大礼包”本质上就是个“怎么买最便宜”的搜索 + 记忆化问题,用 Java 写起来不难,但如果一开始就暴力,十有八九会被时间打爆。
大概意思是这样的:
有
n种商品,每种商品单买有单价price[i]商家搞活动,给了一堆“大礼包”,每个礼包里写着:
各种商品分别送几件 整个礼包一个打包价 你手里有一个
needs[i],表示第 i 种商品你刚好想买多少件问:在可以随便买单品,也可以重复买礼包的前提下,最少要花多少钱,才能刚好满足
needs(不允许买多了扔掉)
听起来就像:我只想买 2 瓶水 3 包薯片,但门口小卖部搞一堆捆绑套餐,怎么算最省钱。
直接暴力会怎样?
第一反应一般是: “要么不用礼包全按单价买,要么枚举所有礼包,能用就用,递归往下算最小花费。”
伪代码脑补一下:
当前剩余需求
needs答案起步价:全部按单价买一遍
枚举每个礼包:
如果礼包里的每种商品数量都 ≤ 当前需求 就尝试“买这个礼包一次”,更新新的需求,再递归算后面的最优价 取所有方案的最小值
问题是:needs 可能有很多种组合,礼包也可以重复用,纯暴力递归会反复算同样的状态,复杂度直接爆炸。
关键思路:记忆化搜索
核心一句话:
“同样的剩余需求,之后能花出的最小钱一定是相同的。”
那我们就可以:
把“当前剩余需求”当成一个状态 对每个状态只算一次,把结果记在 memo里下次再遇到同样的需求,直接查缓存
实现问题在于:needs 是一个数组,不能直接当 HashMap 的 key。通常有两种玩法:
把数组转成字符串,比如 "2,3,1"或者用 List<Integer>作为 key(注意要不可变或自己 new 一个)
我下面用的是字符串,写起来最直观。
一点实用的小优化
有些礼包其实压根没用,比如:
礼包价格 ≥ 礼包里所有商品原价之和
这种礼包永远不可能帮你省钱,可以提前过滤掉,减少搜索分支数。
另外,递归里判断“礼包能不能用一次”的逻辑就是:对每个商品 i,看 needs[i] - special[i] 是否都 ≥ 0,如果有一个负了,这个礼包当前就不能用。
import java.util.*;
publicclassShoppingOffersSolution{
// 主入口,力扣函数签名基本就是这样
publicintshoppingOffers(List<Integer> price,
List<List<Integer>> special,
List<Integer> needs){
// 先把明显不划算的礼包过滤掉
List<List<Integer>> filtered = new ArrayList<>();
int n = price.size();
for (List<Integer> sp : special) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += sp.get(i) * price.get(i);
}
int packPrice = sp.get(n);
if (packPrice < sum) { // 只保留确实比单买便宜的
filtered.add(sp);
}
}
Map<String, Integer> memo = new HashMap<>();
return dfs(price, filtered, needs, memo);
}
// 记忆化搜索:返回“当前 needs 下的最小花费”
privateintdfs(List<Integer> price,
List<List<Integer>> special,
List<Integer> needs,
Map<String, Integer> memo){
String key = encode(needs);
if (memo.containsKey(key)) {
return memo.get(key);
}
int n = price.size();
// 不用任何礼包,全部按单价买,是一个保底答案
int minCost = 0;
for (int i = 0; i < n; i++) {
minCost += needs.get(i) * price.get(i);
}
// 尝试用每一个礼包
for (List<Integer> sp : special) {
List<Integer> nextNeeds = new ArrayList<>(n);
boolean valid = true;
for (int i = 0; i < n; i++) {
int remain = needs.get(i) - sp.get(i);
if (remain < 0) { // 说明这个礼包当前用不了
valid = false;
break;
}
nextNeeds.add(remain);
}
if (!valid) {
continue;
}
int packPrice = sp.get(n);
// 用一次这个礼包 + 之后剩余需求的最优解
int cost = packPrice + dfs(price, special, nextNeeds, memo);
minCost = Math.min(minCost, cost);
}
memo.put(key, minCost);
return minCost;
}
// 把 needs 变成 "2,3,1" 这种字符串作为 key
private String encode(List<Integer> needs){
StringBuilder sb = new StringBuilder();
for (int i = 0; i < needs.size(); i++) {
if (i > 0) sb.append(',');
sb.append(needs.get(i));
}
return sb.toString();
}
// 随便写个 main 做本地测试
publicstaticvoidmain(String[] args){
ShoppingOffersSolution s = new ShoppingOffersSolution();
List<Integer> price = Arrays.asList(2, 5);
List<List<Integer>> special = new ArrayList<>();
special.add(Arrays.asList(3, 0, 5)); // 3 个商品0 + 0个商品1 打包价5
special.add(Arrays.asList(1, 2, 10));
List<Integer> needs = Arrays.asList(3, 2);
System.out.println(s.shoppingOffers(price, special, needs)); // 输出最小花费
}
}
整理一遍就是:
把“当前剩余需求”当成一个状态,写成递归函数 dfs(needs)dfs里先计算“全按单价买”的钱,作为一个上界枚举所有礼包,能用就更新一次需求,递归算后面的价格,取最小 用 Map做记忆化,避免重复计算同一个needs为了减分支,提前把不划算的礼包丢掉
这样写出来,既不难理解,又能很好地通过复杂度这关。
你要是愿意再折腾,可以自己改成“纯 DP”的写法,不过那一般要把需求转为多维数组,代码会丑很多,工作里我一般也就老老实实用记忆化搜索了。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html