题目描述
给一个m*n的矩阵seats表示教室内座位的分布,如果是坏的用“#”表示不能坐人
同时为了避免作弊,学生左,右,左前,右前4个位置不能有人
求给定座位后,最多能坐多少个学生?
提示:
- 1<=m<=8
- 1<=n<=8
f1-动态规划+状态压缩 |
基本分析
- 每一行坐人的状态怎么表示?参考给的数据规模,用二进制来表示坐人的状态
- 怎么满足以上的限制条件?
- 坏的不能坐人?对某一行,预处理座位,把“#”用1代替,“.”用0代替,转化为二进制表达chair,如果chair&当前学生状态state==0,表示不冲突
- 左右不能有人? state & (state >>1)==0 , state &(state<<1)==0
- 左前右前不能有人?拿去前排的状态last_state, 和上面类似
- 怎么根据题目给出的seats表达,预处理出需要的二进制状态表达chair?
- 增加了第0排的状态,都是0
- 后续每排,拿到这一排坏的位置的索引,利用运算把1放到对应的位置上,这里是用到了reduce(函数,列表)方式实现
- 怎么定义dp状态?定义dp[i][state]表示第i排状态是state时候,前i排能坐的最大人数
- 怎么初始化dp状态?增加了一个第0排,chair也增加了第0排,两个可以对上。真正遍历从i=1开始,到i=m结束
- 怎么转移状态?dp[i][state] = max(dp[i][state], dp[i-1][last_state]+bin(state).count('1'))
- 怎么拿到做后的结果?max(dp[m])
代码
class Solution:
def maxStudents(self, seats: List[List[str]]) -> int:
m, n = len(seats), len(seats[0])
# 预处理坏座位
chair = [0]
for i in range(m):
tmp = reduce(lambda x, y:x| 1<<y, [0] + [j for j in range(n) if seats[i][j]=="#"])
chair.append(tmp)
mask = 1<<n
dp = [[0]*mask for _ in range(m+1)]
for i in range(1, m+1):
for state in range(mask):
if state & chair[i] == 0 and state & (state>>1) ==0 and state & (state<<1) ==0:
for last_state in range(mask):
if state & (last_state>>1) ==0 and state & (last_state<<1)==0:
dp[i][state] = max(dp[i][state], dp[i-1][last_state] + bin(state).count('1'))
return max(dp[m])
复杂度
时间:\(遍历m排,每排需要2^n * 2^n, 最终为O(m*2^{2n})\)
空间:\(O(m*2^n)\)
总结
- 用二进制表示人占据座位的情况
- 同样用在预处理的时候用二进制表示座位的占用情况
- 对于以上限制条件,用二进制的运算实现
- 在dp的时候,对初始化dp和chair的时候,都预先留了一个空排,以便可以从上往上顺序完成
- 还可以自顶向下实现,见后续解题