
本题要求从一组员工调岗请求中选出尽可能多的子集,使得所有建筑的净员工变化为零(即每个楼进出人数相等),需通过状态枚举与约束验证求解,贪心或局部计数无法保证全局可行性。
本题要求从一组员工调岗请求中选出尽可能多的子集,使得所有建筑的净员工变化为零(即每个楼进出人数相等),需通过状态枚举与约束验证求解,贪心或局部计数无法保证全局可行性。
这道题的核心约束在于:全局平衡性——不是每栋楼单独满足“进=出”即可,而是所选请求集合必须让 所有 n 座建筑同时满足净变化为 0。这意味着调岗请求之间存在强耦合关系:某栋楼的“出”必须由其他楼的“入”来匹配,而这些“入”又依赖于其他楼是否愿意“出”,形成环状依赖。
你提供的贪心解法:
ans += Math.min(in[i], out[i]);
看似合理(取每栋楼最多能“兑现”的进出对数),但本质错误在于它将问题分解为独立决策,忽略了请求之间的结构性依赖。例如输入 n = 3, requests = [[2,2],[2,1],[1,0]]:
-
in = [1, 1, 0](楼0收1人,楼1收1人,楼2收0人) -
out = [0, 1, 2](楼0无人离开,楼1离1人,楼2离2人) - 贪心计算得
min(1,0)+min(1,1)+min(0,2) = 0+1+0 = 1—— 碰巧答案正确,但逻辑不成立。
⚠️ 关键误区:min(in[i], out[i]) = 1 在楼1处暗示“可实现1次进出”,但该“出”对应请求 [1,0],其“入”在楼0;而楼0的 in[0]=1 却无任何 out[0](即没人从楼0出发),导致楼0净增1人,违反全局平衡!因此 [1,0] 不可单独启用。
真正可行的请求是 [2,2]:员工从楼2出发又回到楼2,对所有楼净变化均为 0(Δ₀=0, Δ₁=0, Δ₂=0)。这是唯一满足条件的单请求子集,故答案为 1。
那么为何必须用回溯(或状态压缩枚举)?因为:
- 可行解是请求集合的子集,共 $2^m$ 种可能($m = \text{len(requests)}$);
- 对每个子集,需检查是否对全部 $n$ 座楼都满足:
∑(from==i) - ∑(to==i) == 0; - 无法通过局部统计预判哪些请求可共存——是否存在合法子集,本质是带约束的子集选择问题,属于 NP 类(虽 $m \leq 16$ 允许指数解,但无已知多项式贪心策略)。
✅ 正确解法(DFS + 回溯)示例:
class Solution {
private int max = 0;
private int[] balance; // balance[i] = 进入i的人数 - 离开i的人数
public int maximumRequests(int n, int[][] requests) {
balance = new int[n];
dfs(0, 0, requests);
return max;
}
private void dfs(int idx, int count, int[][] req) {
if (idx == req.length) {
// 检查是否所有楼平衡
for (int b : balance) {
if (b != 0) return;
}
max = Math.max(max, count);
return;
}
// 选择当前请求:更新 balance
int from = req[idx][0], to = req[idx][1];
balance[from]--;
balance[to]++;
dfs(idx + 1, count + 1, req);
// 回溯:撤销选择
balance[from]++;
balance[to]--;
dfs(idx + 1, count, req);
}
}? 优化提示:可在 DFS 前剪枝——若剩余请求数 + 当前 count ≤ 当前 max,直接返回;也可用状态压缩枚举(for (int mask = 0; mask ),对每个 mask 计算 balance 数组并验证。
总结:本题不可贪心,因“可行”是全局约束;回溯/枚举是标准解法,时间复杂度 $O(2^m \cdot n)$,在 $m \leq 16$ 下完全可行。理解“净变化为零”必须作用于整个选定子集,而非逐楼独立计算,是破题关键。

















