给你一个 m ? n m * n m?n 的矩阵 s e a t s seats seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 ‘#’ 表示;否则,用 ‘.’ 表示。
学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面或者后面的学生的答卷。请你计算并返回该考场可以容纳的同时参加考试且无法作弊的 最大 学生人数。
学生必须坐在状况良好的座位上。
输入:seats =
[[“#”,“.”,“#”,“#”,“.”,“#”],
[“.”,“#”,“#”,“#”,“#”,“.”],
[“#”,“.”,“#”,“#”,“.”,“#”]]
输出:4
解释:教师可以让 4 个学生坐在可用的座位上,这样他们就无法在考试中作弊。
输入:seats =
[[“.”,“#”],
[“#”,“#”],
[“#”,“.”],
[“#”,“#”],
[“.”,“#”]]
输出:3
解释:让所有学生坐在可用的座位上。
输入:seats =
[[“#”,“.”,“.”,“.”,“#”],
[“.”,“#”,“.”,“#”,“.”],
[“.”,“.”,“#”,“.”,“.”],
[“.”,“#”,“.”,“#”,“.”],
[“#”,“.”,“.”,“.”,“#”]]
输出:10
解释:让学生坐在第 1、3 和 5 列的可用座位上。
提示:
相关标签 :位运算、数组、动态规划、状态压缩
今天的问题是一个较为复杂的动态规划问题,还是稍有难度的。
学生在选择座位时,必须满足四个指定的位置都没有人坐,而这四个位置,要不位于当前排,要不位于前一排。因此,某一排的座位上,学生可以选择的座位取决于上一排的落座情况。这提醒我们可以以排为单位来进行动态规划。同时,每一个座位,学生可以选择坐或者不坐,我们可以用一个长为 n n n 的二进制数字来表示某一排的落座情况,从低到高的第 j j j 位,如果为 1 1 1 则表示这一排的第 j j j 个位置有人落座,为 0 0 0 则表示无人落座。
构造函数 d p ( r o w , s t a t u s ) dp(row,status) dp(row,status),用来表示当第 r o w row row 排学生落座情况为 s t a t u s status status 时,第 r o w row row 排及其之前所有座位能够容纳最多的学生数。首先判断第 r o w row row 排的落座情况是否可能为 s t a t u s status status 时,我们可以构造一个函数 i s S i n g l e R o w C o m p l i a n t isSingleRowCompliant isSingleRowCompliant 来辅助判断,主要是判断是否有学生坐了坏的位置和是否有两个学生挨着坐。如果第 r o w row row 排的落座情况不可能为 s t a t u s status status ,返回一个极小的负值。接下来需要对前一排的落座情况进行遍历,即求出所有的 d p ( r o w ? 1 , u p p e r R o w S t a t u s ) dp(row?1,upperRowStatus) dp(row?1,upperRowStatus),并且在这相邻两排的落座情况不会产生作弊的情况下,求出最大的学生数后进行返回。
最后我们调用 d p dp dp ,求出最后一排所有状态下的最大学生数量。因为求解过程中会多次求解同一个状态,所以对动态规划进行记忆化的处理来降低时间复杂度。
class Solution:
def maxStudents(self, seats: List[List[str]]) -> int:
def isSingleRowCompliant(status: int, row: int) -> bool:
for j in range(n):
if ((status >> j) & 1) == 1:
if seats[row][j] == '#':
return False
if j > 0 and ((status >> (j - 1)) & 1) == 1:
return False
return True
def isCrossRowsCompliant(status: int, upperRowStatus: int) -> bool:
for j in range(n):
if ((status >> j) & 1) == 1:
if j > 0 and ((upperRowStatus >> (j - 1)) & 1) == 1:
return False
if j < n - 1 and ((upperRowStatus >> (j + 1)) & 1) == 1:
return False
return True
@cache
def dp(row: int, status: int) -> int:
if not isSingleRowCompliant(status, row):
return -inf
students = bin(status).count('1')
if row == 0:
return students
mx = 0
for upperRowStatus in range(2 ** n):
if isCrossRowsCompliant(status, upperRowStatus):
mx = max(mx, dp(row - 1, upperRowStatus))
return students + mx
m, n = len(seats), len(seats[0])
mx = 0
for i in range(2 ** n):
mx = max(mx, dp(m - 1, i))
return mx