最多可达成的换楼请求数目-python

leetCode第1601题 最多可达成的换楼请求数目
链接:https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests

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

我们先进行分析,假如请求列表为[0,1] [1,0] [1,2] [2,0]
这只有两个可行解,[0,1] [1,0] 和[0,1] [1,2] [2,0]
为了使满足请求数最大,那么我必定选择后者
为了找到最求解,势必需要将所有情况进行枚举,才能找到所有可行解。

既然有了枚举的思路,那么我们该如何进行枚举。接下来就得用到深度优先搜索,即回溯。那么dfs是对树形结构而言的,我们怎么构造这个解空间呢?

当我们采用枚举的时候,对当前请求只有两种处理,就是接受当前请求还是不接受当前请求。我们从0开始为请求编号。
在这里插入图片描述
那么对于0请求,我们有两种情况,考虑和不考虑,之后就到了1请求。
在这里插入图片描述
当然求几个请求就意味着我的树有多少层。
这就是问题的解空间。
可是对于这个树如何找到我的可行解呢,找到之后怎么进行回溯呢?
既然是深度优先搜索的话,我们不妨先考虑不接受当前请求的情况,然后再来考虑接受当前请求的情况。
在这里插入图片描述

当我们不考虑当前请求之后,就该看看考虑当前请求的情况。
在这里插入图片描述
只有当前请求考虑与被考虑两个情况都算进来之后,我才能进行回溯,去看2请求被考虑的情况,然后再看3请求考虑与不考虑的情况
在这里插入图片描述
当2请求考虑与被考虑两种情况都计算完之后,我才能回溯到1请求。

这样通过回溯+枚举的方式就能计算到所有情况。
但怎么样才是一个可行解呢?
我们不妨设一个列表来存储每一栋楼的情况,初始化为0.
当有人离开时,这栋楼的值就要-1,如果有人加入时,值就要+1.
每一栋楼的精变化为0,才是一个可行解,那么列表在不断地+1或-1后还是0的话,就说明这时一个可行解。
然后根据完成的请求数看是否更新最大值。

当然还有回溯中特别重要的一点,就是我回溯到上一个状态时,设置的请求数和存储楼情况的列表也都要回溯到上一状态,所以要注意保存中间结果。

## python3
class Solution:
    def maximumRequests(self, n: int, requests: [[int]]) -> int:
        ## n是几栋楼,request 是发出请求的列表
        ans,cnt,zero = 0,0,n
        delta = [0]*n

        def dfs(pos:int)-> None:
            nonlocal ans,cnt,zero
            if pos == len(requests): ## 遍历到了最后一个请求,搜索到了最底层
                if zero == n : ## 判断zero的值是否还是n,如果是就证明是一个可行解
                   ans = max(ans,cnt)  ## 更新最大值
                return

            ## 不考虑当前请求,直接看下一个
            dfs(pos + 1)

           ## 考虑当前请求
            z = zero  ## 记录当前状态
            cnt += 1  ## 当前请求被处理,所以计数器加一
            x,y = requests[pos]  ## x是要离开的楼,y是要进入的楼
            if delta[x] == 0:
                zero -= 1
            # zero -= delta[x] == 0
            delta[x] -= 1
            if delta[x] == 0 : # 如果是随着人员的离开而使得delta[x]重新回复为0,那么为0的楼数要增加,zero要+1
                zero += 1
            # zero += delta[x] == 0
            ## 同理,对y楼人员的假如进行处理
            if delta[y] == 0:
                zero -= 1
            # zero -= delta[y] == 0
            delta[y] += 1
            if delta[y] == 0:
                zero +=1
            # zero += delta[y] == 0

            ## 这样人员的离开和假如都处理完了,就可以处理下一个请求了
            dfs(pos + 1)

            ## 只有当前请求考虑和不考虑两种情况都处理完之后,我才能进行回溯
            ## 如果要回溯的话,所有值都要还原成原来的状态
            delta[y] -= 1
            delta[x] += 1
            cnt -= 1
            zero = z

        dfs(0)
        return ans
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

unseven

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

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

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

打赏作者

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

抵扣说明:

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

余额充值