POJ P1185 炮兵阵地

题目链接

分析

*看数据显然是状压dp*QwQ

首先分析列数为1的情况:第i行的炮台数分两种情况,当其为山地时,不能放炮台,故f[i]=f[i-1];若其为平原,有放与不放两种情况,放时前两行不能放,故:* f[i]=max(f[i-3]+1,f[i-1])*;
当列数大于1时,我们把上面的思路推广一下,定义合法的状态为当前行没有冲突的炮台,找到当前第i行合法的状态j与上一行i-1行合法的状态k,状态j的炮台数为num[j],且上一行i-1行状态k的最大炮台数通过i-2行状态l得到
在这里我们定义 f[i][j][k] 表示第i行状态为k,第i-1状态为j时的最大炮兵个数。
于是可以推出 方程f[i][k][t]=max(f[i][k][t],f[i-1][j][k]+num[t]);num[t]为t状态中1的个数 必须从边界转移
注意边界条件 f[1][1][i] =num[i]
状态i能够满足第一行的硬件条件(注意:这里的i指的是第i个状态,不是一个二进制数,开一个数组保存二进制状态)

#include<bits/stdc++.h>
#define check(x) ( (x&(x<<1))|(x&(x<<2)) ? 0:1)//判断是否与前面的2个冲突
using namespace std;
int a[105];char s[13];
int cnt=0,stk[65],sum[65];
int n,m,f[105][65][65];
int getSum(int x){
    int num=0;
    for(;x>0;x>>=1)
        if(x&1)num++; 
    return num;
}//计算集合中1的个数
void findStk(int n){
    for(int s=0;s<(1<<n);s++)
        if(check(s)){
            cnt++;
            stk[cnt]=s;//记录每行所有的状态
            sum[cnt]=getSum(s);
        }
}
int main(){
    scanf("%d%d",&n,&m);
    memset(f,-1,sizeof(f));
    for(int i=1;i<=n;i++){
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)
                if(s[j]=='H')a[i]|=(1<<j-1);//预处理每行的状态
        }
    findStk(m);//得到sta数组
    for(int i=1;i<=cnt;i++)
        if(!(stk[i]&a[1]))//判断第一行可以放的可能性
            f[1][1][i]=sum[i];//处理第一行的边界状态
    for(int r=1;r<=n;r++)
        for(int i=1;i<=cnt;i++){
            if(stk[i]&a[r])continue;//当前行和H是否冲突
            for(int j=1;j<=cnt;j++){
                if(stk[i]&stk[j])continue;//当前行和i-1行是否冲突
                for(int k=1;k<=cnt;k++){
                    if(stk[i]&stk[k])continue;//当i-1行和i-2行是否冲突
                    if(f[r-1][k][j]==-1)continue;//不存在状态可以转移
                    f[r][j][i]=max(f[r][j][i],f[r-1][k][j]+sum[i]);
                }
            }
        }
    int ans=0;
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=cnt;j++)
            ans=max(ans,f[n][i][j]);
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值