程序员老鬼

细思极恐!提了离职后公司电脑的灯一直亮着,都要走了,还要被时刻监控~

刚看到个贴子,说有大厂员工提完离职后,公司电脑摄像头的灯一直亮着。嗯…这事确实有点背脊发凉的感觉,谁都不想走都要走了还被盯着看。

Image

这事关键不在“灯亮”,而在“边界感”。网友们有人说可能是系统 bug,也有人说大厂监控本来就严格。我看了看,两个说法都不算离谱,但员工的感受不能被忽略。

毕竟电脑就像你的工位延伸,离职后还亮着就像你已经关门了,结果有人从猫眼里一直往里瞄,能不别扭吗?

换个角度想,公司确实有信息安全压力,但安全和尊重之间还是要有条线。

你要查资料、锁权限都没问题,但别让员工觉得“你走了我还在盯着你的一举一动”。这种气氛久了,只会让大家更心寒。

制度可以严,边界要清晰。职场再卷,也别把最基本的尊重卷没了。【备注:文末可领最新资料】

面试题:大礼包

先说结论:这题“大礼包”本质上就是个“怎么买最便宜”的搜索 + 记忆化问题,用 Java 写起来不难,但如果一开始就暴力,十有八九会被时间打爆。

大概意思是这样的:

  • 有 n 种商品,每种商品单买有单价 price[i]

  • 商家搞活动,给了一堆“大礼包”,每个礼包里写着:

    • 各种商品分别送几件
    • 整个礼包一个打包价
  • 你手里有一个 needs[i],表示第 i 种商品你刚好想买多少件

  • 问:在可以随便买单品,也可以重复买礼包的前提下,最少要花多少钱,才能刚好满足 needs(不允许买多了扔掉)

听起来就像:我只想买 2 瓶水 3 包薯片,但门口小卖部搞一堆捆绑套餐,怎么算最省钱。

直接暴力会怎样?

第一反应一般是: “要么不用礼包全按单价买,要么枚举所有礼包,能用就用,递归往下算最小花费。”

伪代码脑补一下:

  1. 当前剩余需求 needs

  2. 答案起步价:全部按单价买一遍

  3. 枚举每个礼包:

  • 如果礼包里的每种商品数量都 ≤ 当前需求
  • 就尝试“买这个礼包一次”,更新新的需求,再递归算后面的最优价
  • 取所有方案的最小值

问题是:needs 可能有很多种组合,礼包也可以重复用,纯暴力递归会反复算同样的状态,复杂度直接爆炸。

关键思路:记忆化搜索

核心一句话:

“同样的剩余需求,之后能花出的最小钱一定是相同的。”

那我们就可以:

  • 把“当前剩余需求”当成一个状态
  • 对每个状态只算一次,把结果记在 memo 里
  • 下次再遇到同样的需求,直接查缓存

实现问题在于:needs 是一个数组,不能直接当 HashMap 的 key。通常有两种玩法:

  1. 把数组转成字符串,比如 "2,3,1"
  2. 或者用 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)); // 输出最小花费
    }
}

整理一遍就是:

  1. 把“当前剩余需求”当成一个状态,写成递归函数 dfs(needs)
  2. dfs 里先计算“全按单价买”的钱,作为一个上界
  3. 枚举所有礼包,能用就更新一次需求,递归算后面的价格,取最小
  4. 用 Map 做记忆化,避免重复计算同一个 needs
  5. 为了减分支,提前把不划算的礼包丢掉

这样写出来,既不难理解,又能很好地通过复杂度这关。

你要是愿意再折腾,可以自己改成“纯 DP”的写法,不过那一般要把需求转为多维数组,代码会丑很多,工作里我一般也就老老实实用记忆化搜索了。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领,也可以链接我微信:hls404