leetcode
leetcode 2651 ~ 2700
参加考试的最大学生数

参加考试的最大学生数

难度:

标签:

题目描述

Given a m * n matrix seats  that represent seats distributions in a classroom. If a seat is broken, it is denoted by '#' character otherwise it is denoted by a '.' character.

Students can see the answers of those sitting next to the left, right, upper left and upper right, but he cannot see the answers of the student sitting directly in front or behind him. Return the maximum number of students that can take the exam together without any cheating being possible.

Students must be placed in seats in good condition.

 

Example 1:

Input: seats = [["#",".","#","#",".","#"],
                [".","#","#","#","#","."],
                ["#",".","#","#",".","#"]]
Output: 4
Explanation: Teacher can place 4 students in available seats so they don't cheat on the exam. 

Example 2:

Input: seats = [[".","#"],
                ["#","#"],
                ["#","."],
                ["#","#"],
                [".","#"]]
Output: 3
Explanation: Place all students in available seats. 

Example 3:

Input: seats = [["#",".",".",".","#"],
                [".","#",".","#","."],
                [".",".","#",".","."],
                [".","#",".","#","."],
                ["#",".",".",".","#"]]
Output: 10
Explanation: Place students in available seats in column 1, 3 and 5.

 

Constraints:

  • seats contains only characters '.' and'#'.
  • m == seats.length
  • n == seats[i].length
  • 1 <= m <= 8
  • 1 <= n <= 8

代码结果

运行时间: 30 ms, 内存: 16.5 MB


/*
 * Java Stream cannot be directly used to solve this problem efficiently due to its complex dynamic programming nature. 
 * However, we can demonstrate the initial part of the solution to create the mask and check validity using streams.
 */
import java.util.stream.*;
public class SolutionStream {
    public int maxStudents(char[][] seats) {
        int m = seats.length, n = seats[0].length;
        int[] dp = new int[1 << n];
        Arrays.fill(dp, -1);
        dp[0] = 0;
        for (int i = 0; i < m; i++) {
            int[] nextDp = new int[1 << n];
            Arrays.fill(nextDp, -1);
            IntStream.range(0, 1 << n).filter(mask -> dp[mask] != -1).forEach(mask -> {
                IntStream.range(0, 1 << n).forEach(newMask -> {
                    boolean valid = IntStream.range(0, n).allMatch(k -> {
                        if ((newMask & (1 << k)) == 0) return true;
                        if (seats[i][k] == '#') return false;
                        if (k > 0 && (newMask & (1 << (k - 1))) != 0) return false;
                        if (k < n - 1 && (newMask & (1 << (k + 1))) != 0) return false;
                        if (k > 0 && (mask & (1 << (k - 1))) != 0) return false;
                        if (k < n - 1 && (mask & (1 << (k + 1))) != 0) return false;
                        return true;
                    });
                    if (valid) {
                        int count = (int) IntStream.range(0, n).filter(k -> (newMask & (1 << k)) != 0).count();
                        nextDp[newMask] = Math.max(nextDp[newMask], dp[mask] + count);
                    }
                });
            });
            dp = nextDp;
        }
        return Arrays.stream(dp).max().orElse(0);
    }
}

解释

方法:

该题解采用动态编程与位运算相结合的方法来求解。首先,将教室的座位状态从字符数组转换为整数数组,每个整数的二进制位表示一行中的座位是否可用('.' 表示可用,对应位为1)。接着,定义一个递归函数 `dfs` 来尝试所有可能的座位安排。对于每一行,递归函数尝试将学生放在当前可用的座位上,同时确保不违反学生之间的视线要求(不能看到左上、右上、左边、右边的同学)。`dfs` 函数使用记忆化以避免重复计算,提高效率。最终,通过递归检查所有行的所有可能座位安排,找出可以容纳的最大学生数。

时间复杂度:

O(m * 4^n)

空间复杂度:

O(m * 4^n)

代码细节讲解

🦆
题解中的`dfs`函数中的参数`i`, `j`, `k`各代表什么含义?在递归中它们是如何变化的?
`i`代表当前正在处理的行的索引,`j`代表当前行中尚未被选择的可用座位的集合(以二进制形式表示,1代表该位置可用),`k`代表上一行中已经放置学生的座位(同样以二进制形式表示)。在递归中,当函数考虑当前行的一个座位时,将这个座位从`j`中移除。如果选择在这个座位放置学生,则更新`k`来包含这个座位(通过`k | lb`操作),且在下一次调用中,不考虑当前座位及其邻近座位(通过`j & ~(lb * 3)`操作)。当一行处理完毕后,递归调用转向上一行(`i`递减),同时重置`j`为上一行的初始可用座位状态,并继续尝试不同的座位组合。
🦆
在位运算中,`k << 1`和`k >> 1`操作是如何帮助检查左上和右上的座位约束的?这是否意味着左右方向的座位也是被考虑的?
`k << 1`和`k >> 1`分别将`k`中表示上一行学生座位的二进制位向左和向右移动一位,这模拟了检查当前座位的左上和右上位置是否有学生。这种位移操作确保了学生之间不会相互干扰视线。尽管这种检查仅限于左上和右上的座位,递归函数在处理当前行时也会考虑当前座位左边和右边的约束(通过`j & ~(lb * 3)`操作,禁止相邻座位同时被选用),从而确保左右方向的座位约束也被适当处理。
🦆
题解提到`记忆化存储`用于避免重复计算,请问具体是如何实现的?`@cache`装饰器具体是如何工作的?
记忆化存储是通过使用`@cache`装饰器实现的,这是Python标准库中的功能,属于`functools`模块。`@cache`装饰器会自动存储函数的输入参数与对应的输出结果。当这个函数再次被相同的输入参数调用时,不会重新计算函数体,而是直接从缓存中返回之前存储的结果。这样可以显著减少重复计算,特别是在递归函数中处理重叠子问题时非常有效,从而提高整体的计算效率。

相关问题