2019.9.1 炮兵阵地

传送门

明显状压dp 用dp[i][j][k]记录前i行最后一行状态编号是j且倒数第二行状态编号是k最多能放几个

所以我们先初始化dp[1]和dp[2]

其中dp[1][j][0]=bj[1][j] 因为第0行可以当做没选

dp[2][j][k]=bj[1][j]+bj[2][k] bj数组的意义同下文

则当i>2时 dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+bj[i][j])

其中l是枚举的倒数第三行的状态编号,bj[i][j]表示第i行的第j个状态放了多少个

上代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
int num[105][(1<<10)+1],bj[105][(1<<10)+1],ans,n,m,mapp[105][15],dp[105][(1<<10)+1][(1<<10)+1];
bool check(int r,int x)
{
    for(int i=m;i>=1;i--)
    {
        if(!mapp[r][i]&&(x&1))return false;
        x>>=1;
    }
    return true;
}//检查状态是否合法
int getnum(int r,int x)
{
    int res=0;
    while(x)res+=x&1,x>>=1;
    return bj[r][num[r][0]]=res;
}//初始化bj数组
signed main()
{
    scanf("%lld%lld",&n,&m);
    char ch;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>ch;
            if(ch=='P')mapp[i][j]=1;
            else mapp[i][j]=0;
        }
    }
    int maxn=(1<<m)-1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=maxn;j++)
        {
            if(check(i,j)&&!(j&(j<<1))&&!(j&(j<<2)))
            {
                num[i][++num[i][0]]=j;//num[i][j]表示第i行编号为j的状态是什么,num[i][0]表示这一行有多少个合法状态
                if(i==1)dp[i][num[i][0]][0]=getnum(i,j);
                bj[i][num[i][0]]=getnum(i,j);
            }
        }
    }
    for(int i=1;i<=num[2][0];i++)
    {
        for(int j=1;j<=num[1][0];j++)
        {
            int x=num[2][i],y=num[1][j];
            if(x&y)continue;
            dp[2][i][j]=max(dp[2][i][j],bj[2][i]+bj[1][j]);
        }
    }//枚举上下两行,初始化dp[2]
    for(int i=3;i<=n;i++)
    {
        for(int j=1;j<=num[i][0];j++)
        {
            for(int k=1;k<=num[i-1][0];k++)
            {
                for(int l=1;l<=num[i-2][0];l++)
                {
                    int x=num[i][j],y=num[i-1][k],z=num[i-2][l];
                    if((x&y)||(y&z)||(x&z))continue;
                    dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+bj[i][j]);
                }
            }
        }
    }//枚举这一行、上一行、上上行
    for(int i=1;i<=num[n][0];i++)
    {
        for(int j=1;j<=num[n-1][0];j++)
        {
            int x=num[n][i],y=num[n-1][j];
            if(x&y)continue;
            ans=max(ans,dp[n][i][j]);
        }
    }//枚举这一行和上一行的可能
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qxds/p/11443399.html