poj1185 NOI2001 炮兵阵地【状压dp】

15 篇文章 0 订阅
解题思路:

注意到任意一行的布置只与该行及上两行的布置有关,且分析一个列为10(m的最大值)的表,发现一个全为P的空列上一共也只有60种合法的放置方法,所以可以从上往下进行扫描,用二进制枚举当前这一行的状态,用1024*1024的数组表示前两行的放置状态,在都合法的情况下进行转移到另一个表示当前行与上一行的数组即可(考虑滚动数组)。但由于第一行要特殊处理(i-2是负数会数组越界),所以可以改为从下往上dp,记录下两行的状态进行转移。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

int n,m,mx;
char s[105][15];
int f[2][1<<10][1<<10];
bool ok[105][1<<10];

bool check(int p,int now)
{
    if((now&now<<1)||(now&now<<2))return false;
    for(int i=1;i<=m;i++)
        if(s[p][i]=='H'&&(now&(1<<m-i)))return false;
    return true;
}

int count(int now)
{
    int res=0;
    while(now)res+=now&1,now>>=1;
    return res;
}

int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    n=getint(),m=getint();
    mx=1<<m;
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(int i=1;i<=n+2;i++)
        for(int now=0;now<mx;now++)
            ok[i][now]=check(i,now);
    int o=0;
    for(int i=n;i;i--)
    {
        for(int now=0;now<mx;now++)if(ok[i][now])
                for(int t1=0;t1<mx;t1++)if(ok[i+1][t1])
                {
                    int tmp=0;
                    for(int t2=0;t2<mx;t2++)if(ok[i+2][t2])
                        if(!(now&t1)&&!(now&t2))tmp=max(tmp,f[o][t1][t2]);
                    f[o^1][now][t1]=tmp+count(now);
                }
        o^=1;
    }
    int ans=0;
    for(int now=0;now<mx;now++)if(ok[1][now])
        for(int t1=0;t1<mx;t1++)if(ok[2][t1])
            if(!(now&t1))ans=max(ans,f[o][now][t1]);
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值