JAVA练习183-最多可达成的换楼请求数目

我们有 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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

什巳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值