最多可达成的换楼请求数目(2022-2-28)每日一练

1601. 最多可达成的换楼请求数目(2022-2-28)

我们有 n 栋楼,编号从 0n - 1 。每栋楼有若干员工。由于现在是换楼的季节,部分员工想要换一栋楼居住。

给你一个数组 requests ,其中 requests[i] = [fromi, toi] ,表示一个员工请求从编号为 fromi 的楼搬到编号为 toi 的楼。

一开始 所有楼都是满的,所以从请求列表中选出的若干个请求是可行的需要满足 每栋楼员工净变化为 0 。意思是每栋楼 离开 的员工数目 等于 该楼 搬入 的员工数数目。比方说 n = 3 且两个员工要离开楼 0 ,一个员工要离开楼 1 ,一个员工要离开楼 2 ,如果该请求列表可行,应该要有两个员工搬入楼 0 ,一个员工搬入楼 1 ,一个员工搬入楼 2

请你从原请求列表中选出若干个请求,使得它们是一个可行的请求列表,并返回所有可行列表中最大请求数目。

示例 1:

img

输入: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:

img

输入: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操作: 求二进制0b10001的位置,操作结果为2^3也就是8的二进制中,1的位置在第四位,且后边三位为0;例子:12的二进制为0b110012 & -12 = 4= 2^2说明1的位置在第三位,且后边两位为0.

x & 1操作: 求二进制x的末位是否为1,也就是是否为奇数;例子:9 & 1 = 110 & 1 = 0

OK👌接下来开始这道题:因为提示中限制了楼只能有最多20栋,请求最多有16个,所以我们可以暴力枚举。假设每个请求「可行」为1;「不可行」为0。那么最多16个请求也就是最多有2^16 = 65536种组合排列。完全可以枚举所有的情况。

  1. 遍历所有的可能性。假设第13种方案,我们可以将其转化为二进制0b1101,那就说明四个请求,除了第二个,其余的都可行;而这就是第十三种方案的「展开」或者说是「唯一表示」。
  2. 判断当前第i种方案的「可行」方法数是否大于现存「最大可行方法数」max,如果大于我们再检查这个方案是否满足题目条件;反之就跳过继续遍历下一个可能性。
  3. 当满足上述条件时,我们检查第i种方案的「合法性」,如果合法就替换为最大值max;不合法就继续遍历。
  4. 因为检查「方法合法性」需要多次用到,可以提取成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 
}
最小费用最大流

实在看不懂!大佬的题解放这儿了有能力的同学可以研究研究

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值