1601. 最多可达成的换楼请求数目

1601. 最多可达成的换楼请求数目

一、题目描述

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

二、思路分析

这道题我是直接看的题解,说实话是有点被困难的标签吓到了,我想了想以图的方式解决无果后就直接看题解了,官方有两种题解:1.回溯 + 枚举 2.二进制枚举。可以看到两种方法都用了枚举,由于请求数组长度不大,最大16,所以可以直接枚举所有可能。

第一种方法我觉得就是深度遍历吧。

在这里插入图片描述

就类似上面的在这刻二叉树,对于每一个请求,我们都可以选择接收或者不接受,所以就是两种情况,我们进行深度遍历,保存满足要求且请求数最多的方案。

第二种方法是二进制枚举,其实和前面差不多,只不过是处理方法不一样,如果我们有N个请求,则定义一个N位的二进制数,对于每一个二进制数,某一位为1的话代表接受该请求,为0 不接受,同样是记录符合要求的包含最多请求的数。

三、代码

1.回溯 + 枚举
	//记录每栋楼人员进出情况
    int[] enters;
    //ans最终答案,temp每一个分支的结果
    int ans = 0, temp = 0;
    public int maximumRequests(int n, int[][] requests) {
        enters = new int[n];
        resolver(0,requests);
        return ans;
    }
    //count代表第几个请求
    public void resolver(int count, int[][] requests) {
        //所有选择都选完了
        if(count==requests.length) {
            if(judge(enters) && temp > ans) {
                ans = temp;
            }
            return;
        }
        //不选择该请求,直接进入下一个递归
        resolver(count+1, requests);
        //选择该请求,temp++,enters跳整
        temp++;
        enters[requests[count][0]]--;
        enters[requests[count][1]]++;
        resolver(count+1, requests);
        //运行到这里回退了,所以之前的改变也要回退
        temp--;
        enters[requests[count][0]]++;
        enters[requests[count][1]]--;
    }
    public boolean judge(int[] enters) {
        for(int i=0;i< enters.length;i++) {
            if(enters[i]!=0) {
                return false;
            }
        }
        return true;
    }
2 . 二进制枚举

//直接用的官方题解,这熟练的位运算

还有一些方法的使用 ,例如bitCount(mask),Arrays.fill(delta, 0)

bitCount(mask):返回int型整数 i 的二进制表示中 1 的数量

关于 >>> :

>>>为逻辑右移,>>为算数右移 。算数左移右移考虑符号而逻辑右移不考虑符号,左边补0

public static int bitCount(int i) {
        // HD, Figure 5-2
        i = i - ((i >>> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        return i & 0x3f;
    }

Arrays.fill(delta, 0):数组每一项都变为 后面的参数

class Solution {
    public int maximumRequests(int n, int[][] requests) {
        int[] delta = new int[n];
        int ans = 0, m = requests.length;
        for (int mask = 0; mask < (1 << m); ++mask) {
            int cnt = Integer.bitCount(mask);
            if (cnt <= ans) {
                continue;
            }
            Arrays.fill(delta, 0);
            for (int i = 0; i < m; ++i) {
                if ((mask & (1 << i)) != 0) {
                    ++delta[requests[i][0]];
                    --delta[requests[i][1]];
                }
            }
            boolean flag = true;
            for (int x : delta) {
                if (x != 0) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                ans = cnt;
            }
        }
        return ans;
    }
}

       break;
            }
        }
        if (flag) {
            ans = cnt;
        }
    }
    return ans;
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Little BigUs

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

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

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

打赏作者

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

抵扣说明:

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

余额充值