每一行只与上一行有关,看成二进制,同时每一行最多2^8种状态,我们自然想到进行状态压缩
状态转移方程为:
dp[row][state] = max(dp[row-1][last] + state.count())
其中state代表某一个二进制数字,state.count代表当前数字的二进制有多少个1
注意我们需要检查合法性,这里包括:
本行的合法性:不能把学生安排在坏座位上;不能有相邻的学生
两行之间的合法性:如果第一行某个位置安排了学生,则下一行斜向的两个位置不能安排学生
最后的结果就是
max(dp[m][state])
代码
public int maxStudents(char[][] seats) {
int o[] = new int[256], f[][] = new int[9][256], a[] = new int[9];
int n = seats.length, m = seats[0].length, i, j, k, ans = 0;
for (i = 1; i < 256; i++)
// o用来记录某个数的二进制有多少个1
o[i] = o[i >> 1] + (i & 1);
for (int[] ints : f) {
Arrays.fill(ints, Integer.MIN_VALUE);
}
for (i = 0; i < n; i++)
for (j = 0; j < m; j++)
if (seats[i][j] == '#')
// a用二进制表示教室状态
a[i] |= 1 << j;
for (i = f[0][0] = 0; i < n; i++)
// 遍历一行的可能的坐人状态
for (j = 0; j < 1 << m; j++)
// 验证是否有人坐在坏椅子上,验证是否有人坐在相邻位置
if ((j & a[i]) == 0 && (j & j >> 1) == 0 && (j & j << 1) == 0)
for (k = 0; k < 1 << m; k++)
// 与相邻行是否冲突
if ((j & k >> 1) == 0 && (j & k << 1) == 0)
f[i + 1][j] = Math.max(f[i + 1][j], f[i][k] + o[j]);
for (i = 0; i < 1 << m; i++)
ans = Math.max(ans, f[n][i]);
return ans;
}