POJ1185 炮兵阵地 (状压DP)

题面如图所示
在这里插入图片描述
题目大意:

现在有一个n*m的图,P点可以作为根据地,放置士兵以后以该点为中心有个十字架攻击范围为2.
H点不能够放置士兵但是可以被攻击。现在要求对于给定的n * m的图,求能安置最多的士兵数量。且相互不能攻击到。

题目分析:先把地图压缩为二进制形式,对于一行,枚举它的所有状态K【0,2m -1】,记录合法状态.
要求满足K>>1&K为0且K>>2&K为0,因为相邻2以内不能攻击。
记录该状态能摆放的士兵数。即二进制上有多少个1。
记dp[i][j][k] 表示第i行的状态为k,i-1行的状态为j时能摆放的最大士兵数
枚举所有合法状态S与第一行的压缩地图P尝试是否能够兼容,即S是P的子集。记为dp[1][i][1]= people[i] 表示该行放置第i个合法状态的士兵,且上一行不放置任何。
接下来从下一行开始枚举到第n行,枚举三层合法状态j,k,q,依次代表为i-2,i-1,i 的状态,然后判断该三个状态是否会上下攻击,该三个状态能否兼容到自己对应的行的压缩地图。
转移方程dp[i][q][k]=max(dp[i][k][q],dp[i-1][k][j]+people[q])
最终求答案即为max(dp[n][i][j]) 枚举所有状态

代码如下

const int N=(1<<10)+10;
char s[110][20];
ll group[110];
ll can[N];
ll cnt=0;
ll peo[N];
ll dp[105][N][N];
signed main()
{

    ll n,m;
    read(n);
    read(m);

    for(int i=1; i<=n; i++)
    {
        scanf("%s",s[i]);
        for(int j=0; j<m; j++)
        {
            if(s[i][j]=='P')
            {
                group[i]=(group[i]<<1)+1;
            }
            else
            {

                group[i]=(group[i]<<1);
            }

        }
    }
    ll kk=(1<<m)-1;
    for(int i=0; i<=kk; i++)
    {
        if(((i<<1)&i)==0&&((i<<2)&i)==0)
        {
            can[++cnt]=i;
            for(int j=i; j; j=(j>>1))
            {
                if(j&1)
                {
                    peo[cnt]++;
                }
            }
        }

    }
    for(int i=1; i<=cnt; i++)
    {
        if((group[1]&can[i])==can[i])
            dp[1][1][i]=peo[i];

    }

    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<=cnt; j++) // i-2
        {

            for(int k=1; k<=cnt; k++) //i-1
            {

                for(int q=1; q<=cnt; q++) // i
                {
                    if((group[i]&can[q])==can[q]&&(group[i-1]&can[k])==can[k])
                    {

                        if((can[q]&can[k])==0&&(can[q]&can[j])==0&&(can[j]&can[k])==0)
                        {  // printf("Q %lld %lld %lld\n",)
                            dp[i][k][q]=max(dp[i][k][q],dp[i-1][j][k]+peo[q]);

                        }



                    }

                }
            }
        }

    }

    ll ma=0;

    for(int i=1; i<=cnt; i++)
    {

        for(int j=1; j<=cnt; j++)
        {
            ma=max(ma,dp[n][i][j]);
        }
    }

    printf("%lld",ma);


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值