大家好,我是星恒
今天的题目竟然是一道困难题目,看着就不简单,我们的目标是:理解如何做 + 学一些思路!
这次题目涉及的知识:动态规划,状态压缩(位运算)
给你一个 m * n 的矩阵 seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 ‘#’ 表示;否则,用 ‘.’ 表示。学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面或者后面的学生的答卷。请你计算并返回该考场可以容纳的同时参加考试且无法作弊的 **最大 **学生人数。
学生必须坐在状况良好的座位上。
示例 1:
输入:seats = [["#",".","#","#",".","#"],
[".","#","#","#","#","."],
["#",".","#","#",".","#"]]
输出:4
解释:教师可以让 4 个学生坐在可用的座位上,这样他们就无法在考试中作弊。
示例 2:
输入:seats = [[".","#"],
["#","#"],
["#","."],
["#","#"],
[".","#"]]
输出:3
解释:让所有学生坐在可用的座位上。
示例 3:
输入:seats = [["#",".",".",".","#"],
[".","#",".","#","."],
[".",".","#",".","."],
[".","#",".","#","."],
["#",".",".",".","#"]]
输出:10
解释:让学生坐在第 1、3 和 5 列的可用座位上。
提示:
- seats 只包含字符 ‘.’ 和’#’
- m == seats.length
- n == seats[i].length
- 1 <= m <= 8
- 1 <= n <= 8
分析:
这道困难题,我们先说一下总体思路,然后再讨论为什么这样做:
首先,我们说一下数据的存储
- 用 m 表示行数,n 表示列数
- 使用
0,1(0 好座位,1 坏座位)
来表示一排的情况,并使用二进制表示法表示这种情况,一共n位 - 使用
dp[m][2n]
来存储答案可能的所有情况的人数,1 …2n
表示一排可能出现的所有情况(因为这样可以将所有情况的二进制数表示了出来)
好啦,现在我们就可以正式开始这道题
- 遍历每一行,并将符号转化为二进制数字
- 遍历这一行坐人每一种情况
- 这样坐人,是否有人做到了坏凳子上
- 这样坐人,是否有相邻的人做到了旁边
- 这样坐人,是否有前面一排的人相邻
- 遍历寻找可行的前排可行情况,判断左上和右上是否有人
- 如果这些条件都满足,我们看这种可行前排情况下,和其他前排可行情况哪个大,我们选最大的那个
- 求得了当前可行情况下的最佳坐的人数(这里是前面所有人的人数)
- 这里就求的了这一排所有情况下,每一种情况的最佳坐人人数
- 遍历这一行坐人每一种情况
- 遍历
dp[m][2n]
的最后一排,求他们的最大值
题解:
class Solution {
public int maxStudents(char[][] seats) {
final int m = seats.length, n = seats[0].length;
int[][] dp = new int[m + 1][1 << n];
for (int i = 1; i <= m; i++) {
int invalid = 0;
for (int j = 0; j < n; j++) {
if (seats[i - 1][j] == '#') {
invalid |= 1 << j;
}
}
for (int j = 0; j < (1 << n); j++) {
int adjacentMask = j << 1;
if ((j & invalid) != 0 || (j & adjacentMask) != 0) {
dp[i][j] = -1;
continue;
}
int theOtherAdjacentMask = j >> 1;
for (int s = 0; s < (1 << n); s++) {
if (dp[i - 1][s] == -1) {
continue;
}
if ((s & adjacentMask) != 0 || (s & theOtherAdjacentMask) != 0) {
continue;
}
dp[i][j] = Math.max(dp[i][j], dp[i - 1][s] + Integer.bitCount(j));
}
}
}
int max = 0;
for (int i = 0; i < 1 << n; i++) {
max = Math.max(max, dp[m][i]);
}
return max;
}
}