1601. 最多可达成的换楼请求数目(2022-2-28)
我们有 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
解题思路
二进制枚举
首先我们需要知道一些二进制运算:
<<
左移: 将二进制数向左移动一位,相当于十进制乘2
>>
右移: 将二进制数向右移动一位,相当于十进制除以2并向下取整
8 & -8
操作: 求二进制0b1000
中1
的位置,操作结果为2^3
也就是8的二进制中,1
的位置在第四位,且后边三位为0
;例子:12
的二进制为0b1100
,12 & -12 = 4= 2^2
说明1
的位置在第三位,且后边两位为0.
x & 1
操作: 求二进制x
的末位是否为1
,也就是是否为奇数;例子:9 & 1 = 1
和10 & 1 = 0
OK👌接下来开始这道题:因为提示中限制了楼只能有最多20栋,请求最多有16个,所以我们可以暴力枚举。假设每个请求「可行」为1
;「不可行」为0
。那么最多16个请求也就是最多有2^16 = 65536
种组合排列。完全可以枚举所有的情况。
- 遍历所有的可能性。假设第13种方案,我们可以将其转化为二进制
0b1101
,那就说明四个请求,除了第二个,其余的都可行;而这就是第十三种方案的「展开」或者说是「唯一表示」。 - 判断当前第
i
种方案的「可行」方法数是否大于现存「最大可行方法数」max
,如果大于我们再检查这个方案是否满足题目条件;反之就跳过继续遍历下一个可能性。 - 当满足上述条件时,我们检查第
i
种方案的「合法性」,如果合法就替换为最大值max
;不合法就继续遍历。 - 因为检查「方法合法性」需要多次用到,可以提取成
check
方法;还有将方案展开为二进制,并计算其中1
的数量的方法,我们可以提取成getT
方法
var rs = [],N = 0
var maximumRequests = function(n, requests) {
rs = requests,N = n
let max = 0
// 一个小优化 不过无所谓的
//let arr = requests.filter(v => v[0] != v[1])
for(let i = 0 ; i< (1 << rs.length); i++){// 其实就是2^rs.length
let num = getT(i)
if(num < max) continue
max = check(i) ? cnt : max
}
return max
};
var check = function (c){
let _state = new Array(N).fill(0)
for(let i = 0; i < 16;i++){
// 前边说过这个,末尾位是否为1,可行才会将请求实现
if((c >> i) & 1 == 1) {
_state[rs[i][1]]--
_state[rs[i][0]]++
}
}
// 验证这个方案最终是否满足 每栋楼员工净变化为 0
return _state.some(v=> v!=0) ? false : true
}
var getT = function(c){
let num =0
// 13 = 0b1101 统计1的个数,然后减去刚才统计的1
// 13 => 12 => 8 => 0
// 0b1101 => 0b1100 => 0b1000 => 0b0000
for(let i = c; i> 0; i -= (i & -i)) num++
return num
}
最小费用最大流
实在看不懂!大佬的题解放这儿了有能力的同学可以研究研究