leetcode 1349--状态压缩DP
给你一个 m * n 的矩阵 seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 '#' 表示;否则,用 '.' 表示。
学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面或者后面的学生的答卷。请你计算并返回该考场可以容纳的一起参加考试且无法作弊的最大学生人数。
学生必须坐在状况良好的座位上。
每一行我们都可以用二进制位表示,1表示可以做,0表示不可以做。学生最终的状态也可以用二进制表示,1表示有学生,0表示无学生。那么学生状态一定是椅子状态的子集。例如椅子1001,那么学生0101就是一个错误的状态。我们用(x&y)==x表示x是否是y的子集
此外,对于每一行,我们要求相邻的元素不能相同,即(x&(x>>1))检查状态x是否有两个相邻的1
此外,还要求前一行的左上,右上不能有相同的元素,于是有(x&(y>>1))和((x>>1)&y)表示
我们用dp[i][j]表示前i行,当前状态为j时最大的学生数量,那么状态转移方程为dp[i][j]=max(dp[i-1][k]+invalid(j),dp[i][j]);invalid(j)表示状态j的二进制位上1的数目
代码如下:
int maxStudents(vector<vector<char>>& seats) { int m=seats.size(); int n=seats[0].size(); vector<vector<int>>dp(m+1,vector<int>(1<<n,-1)); vector<int>valid;//初始每行状态 for(int i=0;i<m;i++) { int cur=0;
//每行初始状态 for(int j=0;j<n;j++) { cur=cur*2+(seats[i][j]=='.'); } valid.push_back(cur); } dp[0][0]=0; for(int i=1;i<=m;i++) { int val=valid[i-1]; for(int j=0;j<(1<<n);j++) //枚举所有状态 { if((j&val)==j&&!(j&(j>>1)))//必须是可行状态的子集以及同一行之间不能存在相邻元素 { for(int k=0;k<(1<<n);k++) //状态j由状态k转移过来 { if(!(j&(k>>1))&&!((j>>1)&k)&&dp[i-1][k]!=-1)//左上和右上不能相同 { dp[i][j]=max(dp[i][j],dp[i-1][k]+__builtin_popcount(j));//__builtin_popcount可以计算出状态j的有效位数 } } } } } int ans=0; for(int i=0;i<(1<<n);i++) ans=max(ans,dp[m][i]); return ans; }