洛谷2704 炮兵阵地 状压dp

题目链接
题意:
给你一个 nm n ∗ m 的网格,每个各自最多放一个炮兵,有一些格子不能放,并且一个炮兵的上下左右两个内不能放炮兵,问最多放多少炮兵。

题解:
这个题搜索会超时,那么我们发现 m<=10 m <= 10 是可以状压的,那么就考虑状压dp。但是这道题与常规状压dp的不同点是这道题的每一行状态在转移时要看之前两行的状态,所以这道题要状压两行。
我们首先可以预处理出对于同一行上哪些状态是合法的,把他们记录下来,我们的做dp时只枚举这些合法状态即可。我们设 dp[i][j][k] d p [ i ] [ j ] [ k ] 表示第 i i 行,当前状态的 j j ,上一行的状态是 k k 最多放多少炮兵。我们从外层到内层依次枚举当前行数、当前行状态、上一行状态、上两行状态,那么有状态转移方程 dp[i][j][k]=max(dp[i1][k][l]) d p [ i ] [ j ] [ k ] = m a x ( d p [ i − 1 ] [ k ] [ l ] ) ,其中要求当前行状态 j j 没有在当前行要求的不能放的地方放炮兵,并且状态 j j k k l l 三者两两之间没有矛盾。
最后的答案就是枚举最后一行和前一行所有合法的状态的最大结果,即 max(dp[n][j][k]) m a x ( d p [ n ] [ j ] [ k ] )
代码:

#include 
using namespace std;

int n,m,sta[1002],ans,ji;//sta存同一行合法状态的十进制数,ji存合法状态个数 
int dp[101][1002][1002]; 
//表示第i行,当前行状态是第j个合法状态,上一行状态是第k个合法状态时最多放多少 
//dp数组可以滚动数组优化(可能MLE) 
char s[101];
int mp[101];//也是一个状压后的状态表示每个位置是山地还是平原 
int cnt[1002];//记录每个合法状态放了多少个炮兵 
inline int cal(int x)
{
    int y=1,shu=0;
    while(y<=x)
    {
        if(x&y)
        ++shu;
        y<<=1;
    }
    return shu;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<(1<if((i&(i<<1))==0&&(i&(i<<2))==0&&(i&(i>>1))==0&&(i&(i>>2))==0)      
        {
            sta[++ji]=i;
            cnt[ji]=cal(i);
        }
    }
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s);
        for(int j=0;jif(s[j]=='H')
            mp[i]+=(1<for(int i=1;i<=ji;++i)
    {
        if((sta[i]&mp[1])==0)
        dp[1][i][0]=cnt[i];
    }
    for(int i=1;i<=ji;++i)
    {
        for(int j=1;j<=ji;++j)
        {
            if((sta[i]&sta[j])==0&&(sta[j]&mp[2])==0)
            dp[2][j][i]=max(dp[2][j][i],dp[1][i][0]+cnt[j]);
        }
    }
    for(int i=3;i<=n;++i)
    {
        for(int j=1;j<=ji;++j)
        {
            if((sta[j]&mp[i])==0)
            {
                for(int k=1;k<=ji;++k)
                {
                    if((sta[j]&sta[k])==0)
                    {
                        for(int l=1;l<=ji;++l)
                        {
                            if((sta[l]&sta[k])==0&&(sta[l]&sta[j])==0)
                            dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+cnt[j]);
                        }
                    }
                }
            }
        }
    }
    for(int i=1;i<=ji;++i)
    {
        for(int j=1;j<=ji;++j)
        ans=max(ans,dp[n][i][j]);
    }
    printf("%d\n",ans);
    return 0;
}

你可能感兴趣的:(dp,状压dp)