【经典】带障碍的铺砖块——LEETCODE 覆盖

很经典的题,但是好久没做这类有点忘了。。

经典状压dp做法:用S表示一行的状态,某位为1表示该位被占用,反之表示该位未被占用

dp[i][S]表示第i行状态为S时的最大覆盖数,那么枚举第i-1行的状态S',如果S,S'都合法,那么此时可以求出S状态下最多可以放多少块砖

预处理出cnt[S1][S2]表示上一行是S1,下一行是S2情况下,S2上的最大填放数,填放策略:先放竖的再放横的

然后dp时对于两个状态就可以o(1)转移了

二分图做法:进行黑白染色,每个地板抽象成一个点,黑白格分成两部分

显然可以在相邻两个地板之间连边,如果是障碍物就不能连,匈牙利算法找下最大匹配即可

class Solution {
public:
   
    int cnt[1<<8][1<<8];
    int mp[100][100],block[100];
    void prework(int m){
        for(int S1=0;S1<(1<<m);S1++)
        for(int S2=0;S2<(1<<m);S2++){//上下状态为S1 S2时下放最多填放数量 
            int num=0,T=S2;
            for(int i=0;i<m;i++)//把竖的填了 
                if(!(S1>>i & 1) && (T>>i & 1))num++,T-=(1<<i); 
            for(int i=0;i<m-1;i++)
                if((T>>i&1) && (T>>(i+1)&1)){
                    num++;
                    i++;
                } 
            cnt[S1][S2]=num;
        }
    }
    
    int dp[50][1<<8];
    int domino(int n, int m, vector<vector<int>>& broken) {
        prework(m);
        for(auto v:broken)mp[v[0]][v[1]]=1;
        for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(mp[i][j])block[i]|=(1<<j);
            
        memset(dp,-1,sizeof dp);    
        
        for(int i=0;i<n;i++){
            for(int S=0;S<(1<<m);S++){
                int f=0;
                for(int j=0;j<m;j++)
                    if(!(S>>j & 1) && mp[i][j])f=1;
                if(f)continue;
                
                int T=S-block[i];//状态S可以自由填放的格子
                if(i==0)
                    dp[i][S]=max(dp[i][S],cnt[(1<<m)-1][T]);    
                else {
                    for(int S2=0;S2<(1<<m);S2++)
                        if(dp[i-1][S2]!=-1)
                            dp[i][S]=max(dp[i][S],dp[i-1][S2]+cnt[S2][T]); 
                }
            }
        }
        
        int res=0;
        for(int i=0;i<(1<<m);i++)
            res=max(res,dp[n-1][i]);
        return res;
    }
};
原文地址:https://www.cnblogs.com/zsben991126/p/13039541.html