Leetcode 1349:参加考试的最大学生数(超详细的解法!!!)
in leetcode with 0 comment

Leetcode 1349:参加考试的最大学生数(超详细的解法!!!)

in leetcode with 0 comment

给你一个 m * n 的矩阵 seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 '#' 表示;否则,用 '.' 表示。

学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面或者后面的学生的答卷。请你计算并返回该考场可以容纳的一起参加考试且无法作弊的最大学生人数。

学生必须坐在状况良好的座位上。

示例 1:

输入:seats = [["#",".","#","#",".","#"],
              [".","#","#","#","#","."],
              ["#",".","#","#",".","#"]]
输出:4
解释:教师可以让 4 个学生坐在可用的座位上,这样他们就无法在考试中作弊。 

示例 2:

输入:seats = [[".","#"],
              ["#","#"],
              ["#","."],
              ["#","#"],
              [".","#"]]
输出:3
解释:让所有学生坐在可用的座位上。

示例 3:

输入:seats = [["#",".",".",".","#"],
              [".","#",".","#","."],
              [".",".","#",".","."],
              [".","#",".","#","."],
              ["#",".",".",".","#"]]
输出:10
解释:让学生坐在第 1、3 和 5 列的可用座位上。

提示:

解题思路

首先不难想到组合做法,也就是枚举每个座椅不放两种状态,然后最后求最优解。类似问题Leetcode 39:组合总和

class Solution:
    def maxStudents(self, seats: List[List[str]]) -> int:
        r, c = len(seats), len(seats[0])
        data = [(i, j) for i in range(r) for j in range(c) if seats[i][j] == '.']
        n = len(data)
        fan = set()

        def dfs(u):
            if u == n:
                return 0

            res = dfs(u + 1) # 不妨椅子
            i, j = data[u]
            for x, y in [[0, -1], [0, 1], [-1, -1], [-1, 1]]:
                nx, ny = i + x, j + y
                if 0 <= nx < r and 0 <= ny < c and (nx, ny) in fan:
                    break
            else:
                fan.add(data[u])
                res = max(res, dfs(u + 1) + 1) # 可以放椅子
                fan.remove(data[u])
            return res
        
        return dfs(0)

但是这么做的时间复杂度太高了。那么思考是不是有重叠计算?当前位置这一行最多可以坐多少人,由前一行的状态决定(和后一行没有关系)。有了这个依赖关系,我们可以通过记忆化去优化代码。

from functools import lru_cache
class Solution:
    def maxStudents(self, seats: List[List[str]]) -> int:
        r, c = len(seats), len(seats[0])

        @lru_cache(None)
        def dfs(x, y, pre, cur):
            if x == r:
                return 0

            if not y:
                pre, cur = cur, 0

            nex, ney = (x, y + 1) if y < c - 1 else (x + 1, 0)

            res = dfs(nex, ney, pre, cur)
            if seats[x][y] == '#':
                return res

            for i, j in ((0, -1), (-1, 1), (-1, -1)):
                nx, ny = x + i, y + j
                if 0 <= nx < r and 0 <= ny < c and \
                    ((nx == x and cur & 1 << ny) or \
                    (nx != x and pre & 1 << ny)):
                    break
            else:
                res = max(res, dfs(nex, ney, pre, cur | 1 << y) + 1)
            return res
        
        return dfs(0, 0, 0, 0)

其实当我看到这个问题的时候,首先想到的是将集合拆分成两个对立的集合,然后求最大的那个集合个数,这不就是二分图的最大匹配吗?这里我们将奇数列和偶数列看成对立面更好处理(注意不能将奇数行和偶数行看成对立面,原因在于题目的中的检测方向,横向可以向左向右,而纵向只能向上)。

在构建边的时候,通过偶数列的点指向奇数列的点。对于每个座椅可能存在6条边(左侧、右侧、左上、右上、左下、右下),最后使用匈牙利算法即可。

需要注意的是,我们得到的最大匹配是不能摆放椅子的个数,最后的答案是总椅子数-最大匹配数

class Solution:
    def maxStudents(self, seats: List[List[str]]) -> int:
        r, c = len(seats), len(seats[0])
        match = [[-1] * c for _ in range(r)]
        
        def find(node, vis):
            x, y = node
            for i, j in [[-1, -1], [0, -1], [1, -1], [-1, 1], [0, 1], [1, 1]]:
                nx, ny = i + x, j + y
                if 0 <= nx < r and 0 <= ny < c and not vis[nx][ny] and seats[nx][ny] == '.':
                    vis[nx][ny] = True
                    if match[nx][ny] == -1 or find(match[nx][ny], vis):
                        match[nx][ny] = node
                        return True
            return False
        
        res, cnt = 0, 0
        for i in range(0):
            for j in range(0, c, 2):
                if seats[i][j] != '.': continue
                vis = [[False] * c for _ in range(r)]
                if find((i, j), vis): res += 1
        
        for i in range(r):
            for j in range(c):
                if seats[i][j] == '.': cnt += 1
        return cnt - res

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

「如果我的文章对你有很大帮助,那么不妨~!」

coordinate

谢谢老板O(∩_∩)O~

使用微信扫描二维码完成支付

Responses