LeetCode 1349. 参加考试的最大学生数

给你一个 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-students-taking-exam
 

解法:状态压缩dp。

这是写状压dp的第二题,状态都是用二进制表示的。dp[i][state],表示第i行状态为state时,最多学生数目。写位运算符的时候一定要注意优先级,&、|、^、都是小于==

class Solution {
	
public:
	int maxStudents(vector<vector<char>>& seats) {
		int m = seats.size(), n=seats[0].size();
		vector<vector<int>>dp(m + 1, vector<int>(1 << n));
		int pre = 0;
		for (int i = 1; i <= m; ++i)
		{
			int S = 0;
			for (int j = 0; j < n; ++j)
			{
				if (seats[i - 1][j] == '.')
					S += (1 << j);
			}
			for (int mask = S; mask >= 0; mask = (mask - 1)&S) //在S的子状态中搜索,一般来讲mask应!=0,但本题需要
			{
				int tL = mask << 1, tR = mask >> 1;
				if ((tL&mask) || (tR&mask))
					continue;
				int cnt = __builtin_popcount(mask); 
				for (int maskP = pre; maskP >= 0; maskP = (maskP - 1)&pre) //在i-1行中的子状态中搜索
				{
					if ((tL&maskP) || (tR&maskP))
						continue;
					dp[i][mask] = max(dp[i][mask], dp[i - 1][maskP] + cnt);
                    if(maskP==0)
                        break;
				}
                if(mask==0)     //mask==0时break
                    break;
			}
			pre = S;
		}
		int ans = 0;
		for (int mask = pre; mask >= 0; mask = (mask - 1)&pre)
		{
			ans = max(dp[m][mask], ans);
            if(mask==0)
                break;
		}
		return ans;
	}
};

记忆化+状态压缩。可以根据dp解法进行更改。我们可以发现,第i行的安排情况只能影响到第i+1行的安排情况。dfs(seat, cur,m)表示第cur-1行状态为seat时,能安排最多的学生数。还需加个记忆化。

class Solution {

	int getBin(const vector<char> &seat, const int &n)
	{
		int ans = 0;
		for (int i = 0; i < n; ++i)
		{
			if (seat[i] == '.')
				ans |= (1 << i);
		}
		return ans;
	}

	vector<int> row_seats;
    vector<vector<int>> memo;
	int dfs(int seat, int cur, const int &m)
	{
		if (cur == m)
			return 0;
        if(memo[cur][seat]!=-1)
            return memo[cur][seat];
		int mask = row_seats[cur];
		int ans = 0;
		for (int sub = mask; sub >= 0; sub = (sub - 1)&mask)
		{
			if ((seat << 1)&sub || (seat >> 1)&sub||(sub>>1)&sub||(sub<<1)&sub)
				continue;
			ans = max(ans, dfs(sub, cur + 1, m)+__builtin_popcount(sub));
			if (sub == 0)
				break;
		}
        memo[cur][seat]=ans;
		return ans;
	}


public:
	int maxStudents(vector<vector<char>>& seats) {
		int m = seats.size(), n = seats[0].size();
        memo.resize(m, vector<int>(1<<n, -1));
		for (int i = 0; i < m; ++i)
			row_seats.push_back(getBin(seats[i], n));
		return dfs(0, 0, m);
	}
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值