原题链接
题目描述
给你一个 m * n 的矩阵 seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 ‘#’ 表示;否则,用 ‘.’ 表示。
学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面或者后面的学生的答卷。请你计算并返回该考场可以容纳的一起参加考试且无法作弊的最大学生人数。
学生必须坐在状况良好的座位上。
输入:seats = [["#",".","#","#",".","#"],
[".","#","#","#","#","."],
["#",".","#","#",".","#"]]
输出:4
解释:教师可以让 4 个学生坐在可用的座位上,这样他们就无法在考试中作弊。
题目解法
真的惭愧,之前做contest做到这道hard题一点思路都没有,然后leetcode做题就停滞不前了,已经好长时间了,今天终于回来把这题啃下来了。
真的是非常非常感谢discussion里的那个vote数最多的大佬(https://leetcode.com/problems/maximum-students-taking-exam/discuss/503686/A-simple-tutorial-on-this-bitmasking-problem),讲解得非常清晰,跟着他的思路,参考下面有人实现的Java版本终于是AC了。
当然纯看别人的做法也不能说我就是懂了,只能现在这自己理理思路写一写,过两天再回来做一下试试看。
参考的解法用的是bitmask + dynamic programming
,一个我没有接触过,一个我一直都不太懂×
简单理解如下:
bitmask
:可以说是非常“程序员”的一种思想,要点就在于用二进制来翻译问题并用位操作来解决问题。通常用于题目中某个实体有“对立”的两种状态,比如这道题目中的seat分为“可用”和“不可用”,那么就可以用1
表示可用,0
表示不可用。
dynamic programming
:将大问题分解为相似的子问题,用合理的方法解决每一个子问题,就相当于解决了大问题。比如这道题目中,要使得考场可以容纳最多的学生,那么每一排就要容纳尽可能多的学生。
解题思路简单归纳如下:
- 将每一排椅子的情况都用二进制表示出来(谁能想到我一开始看代码就卡在这里呢 ……主要是要有一个意识,就是尽管我们看到的是十进制数,但事实上在计算机内部存储的还是二进制形式,所以其实我们只要自己脑子里有这样一个一排代表的二进制序列,用一个int数把它存起来就行
- 暴力试探。我觉得这个方法总结下来就这四个字。(首先要建立一个意识就是现在每一排都是一个二进制序列,也就是一个十进制数)首先确定每一排所能表示的最大值,然后从0开始,暴力试探。试探到的数(二进制串)要满足两个基本要求:
2.1 是该排座位情况的子集(就是坏的椅子位上不能坐人 / 不能为1)
2.2 左右不能坐人
满足这两个要求之后,如果这一排不是第一排,还有进一步的要求:
2.3 左上不能坐人
2.4 右上不能坐人
记录每一个可能的二进制串所导致的学生总人数并选取最大值
code如下:
class Solution {
public int maxStudents(char[][] seats) {
int m = seats.length, n = seats