leetcode 1349--状态压缩DP

给你一个 m * n 的矩阵 seats 表示教室中的座位分布。如果座位是坏的(不可用),就用 '#' 表示;否则,用 '.' 表示。

学生可以看到左侧、右侧、左上、右上这四个方向上紧邻他的学生的答卷,但是看不到直接坐在他前面或者后面的学生的答卷。请你计算并返回该考场可以容纳的一起参加考试且无法作弊的最大学生人数。

学生必须坐在状况良好的座位上。

每一行我们都可以用二进制位表示,1表示存在学生,0表示不存在或不能存在(#)

那么每一行都有一个初始状态,之后每一行所有的状态都是当前状态的子集,我们用(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++)
                    {
                        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;
    }
原文地址:https://www.cnblogs.com/flightless/p/12309522.html