我们有 n 栋楼,编号从 0 到 n - 1 。每栋楼有若干员工。由于现在是换楼的季节,部分员工想要换一栋楼居住。
给你一个数组 requests ,其中 requests[i] = [fromi, toi] ,表示一个员工请求从编号为 fromi 的楼搬到编号为 toi 的楼。
一开始 所有楼都是满的,所以从请求列表中选出的若干个请求是可行的需要满足 每栋楼员工净变化为 0 。意思是每栋楼 离开 的员工数目 等于 该楼 搬入 的员工数数目。比方说 n = 3 且两个员工要离开楼 0 ,一个员工要离开楼 1 ,一个员工要离开楼 2 ,如果该请求列表可行,应该要有两个员工搬入楼 0 ,一个员工搬入楼 1 ,一个员工搬入楼 2 。
请你从原请求列表中选出若干个请求,使得它们是一个可行的请求列表,并返回所有可行列表中最大请求数目。
示例 1:
输入:n = 5, requests = [[0,1],[1,0],[0,1],[1,2],[2,0],[3,4]]
输出:5
解释:请求列表如下:
从楼 0 离开的员工为 x 和 y ,且他们都想要搬到楼 1 。
从楼 1 离开的员工为 a 和 b ,且他们分别想要搬到楼 2 和 0 。
从楼 2 离开的员工为 z ,且他想要搬到楼 0 。
从楼 3 离开的员工为 c ,且他想要搬到楼 4 。
没有员工从楼 4 离开。
我们可以让 x 和 b 交换他们的楼,以满足他们的请求。
我们可以让 y,a 和 z 三人在三栋楼间交换位置,满足他们的要求。
所以最多可以满足 5 个请求。
示例 2:
输入:n = 3, requests = [[0,0],[1,2],[2,1]]
输出:3
解释:请求列表如下:
从楼 0 离开的员工为 x ,且他想要回到原来的楼 0 。
从楼 1 离开的员工为 y ,且他想要搬到楼 2 。
从楼 2 离开的员工为 z ,且他想要搬到楼 1 。
我们可以满足所有的请求。
示例 3:
输入:n = 4, requests = [[0,3],[3,1],[1,2],[2,0]]
输出:4
提示:
- 1 <= n <= 20
- 1 <= requests.length <= 16
- requests[i].length == 2
- 0 <= fromi, toi < n
分析:
方法:DFS + 回溯
解决这种题最简单的方式就是枚举出所有情况,然后记录最大的满足人数。我们可以定义一个数组 delta 来记录每层楼的搬入搬出情况,搬入就加 1,搬出就减 1,当每层楼的搬入搬出都为 0 时,就表示该情况可以满足某部分人的要求,用此时的人数和最大人数比较,记录最大值。
我们可以采用深度遍历(DFS)的方式,不选择该请求就直接下一个,选择该请求就将对应楼层加减在下一个,当遍历次数大于数组长度时停止遍历,记录最大人数。遍历完某个请求要在最后恢复对应楼层的加减。
时间复杂度:O(2^m) m 为数组长度,每一个方案有选和不选两种状态,组合方式为 2^m
空间复杂度:O(n+m) n 为楼的个数
class Solution {
//创建差值数组
int[] delta;
//请求数组
int[][] requests;
//记录最大人数,记录被选择次数
int max = 0, select = 0;
public int maximumRequests(int n, int[][] requests) {
delta = new int[n];
this.requests = requests;
dfs(0);
return max;
}
public void dfs(int n){
//遍历完成
if(n == requests.length){
//都为0时记录个数
for(int i : delta){
//不为0直接返回
if(i != 0){
return;
}
}
max = Math.max(select, max);
return;
}
//不采用当前请求继续遍历
dfs(n+1);
//采用当前请求
delta[requests[n][0]]--;
delta[requests[n][1]]++;
select++;
//继续遍历
dfs(n+1);
//回溯
delta[requests[n][0]]++;
delta[requests[n][1]]--;
select--;
}
}
方法2:位运算
因为题目给定范围 1 <= requests.length <= 16,而 requests 只有选中和不被选中两种状态,因此我们可以用一个二进制数来表示 requests 选中情况,而 requests 的长度为 m,那么 requests 的组合就有 2^m 种,只要枚举这些组合,判断这些组合能否每个楼层为 0,取 1 的个数的最大值即可。
时间复杂度:O(n * 2^m) m 为数组长度,n 为楼的个数
空间复杂度:O(n)
class Solution {
int[][] requests;
int n;
public int maximumRequests(int n, int[][] requests) {
this.requests = requests;
this.n = n;
//定义遍历次数和最大人数
int len = 1 << requests.length, max = 0;
//遍历requests
for(int i = 1; i < len; ++i){
int temp = Integer.bitCount(i);
if(temp > max && check(i)){
max = Math.max(max, temp);
}
}
return max;
}
//检查是否能让所有楼层为0
public boolean check(int m){
//差值数组
int[] delta = new int[n];
int sum = 0;
//遍历
for(int i = 0; i < requests.length; ++i){
//被选中
if(((m >> i) & 1) == 1){
if(++delta[requests[i][0]] == 1){
sum++;
}
if(--delta[requests[i][1]] == 0){
sum--;
}
}
}
return sum == 0;
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests