POJ 1185 炮兵阵地(简单状压dp)

一道简单的状压dp看了别人的思路还做了好久,有那么一段时间很绝望,哎

POJ - 1185

和poj3254很像的一道题,设平原为1山地为0,第i行只和i-1与i-2行状态有关,所以我们开一个三维数组dp[i][j][k]表示第i行为状态k,第i-1行为状态j时的情况,首先预处理,同一行的1中间最少隔两个0,这个运用位运算很容易实现,再就是计算合理的情况中,每一种情况中有几个位置可以放炮台,就是数二进制数中有几个1,也很容易实现,剩下的就和poj3254一样了,初始化第一行的状态,然后依次向下更新新的状态

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
char a[105][12];
int state[70];
int num[105];
int dp[105][70][70];
int cur[105];
int top;
int n, m;
bool ok(int x)//判断题目条件
{
    return (x&(x<<1))?0:(x&(x<<2)?0:1);
}
void init()//保留符合题目情况的十进制数
{
    top = 0;
    int total = 1 << m;
    for(int i = 0; i < total; i ++)
        if(ok(i))
        state[++top] = i;
    memset(dp, 0, sizeof(dp));
}
bool fit(int x, int k)//是否匹配
{
    return (x & cur[k]) ? 0 : 1;
}
int countnum(int x)
{
    int cnt = 0;
    while(x)
    {
        x&=(x-1);
        cnt ++;
    }
    return cnt;
}
int main()
{
    while(scanf("%d%d",&n,&m) != EOF && (n + m))
    {
        init();
        for(int i = 1; i <= n; i ++)
            scanf("%s",a[i]+1);
        for(int i = 1; i <= n; i ++)
        {
            cur[i] = 0;
            for(int j = 1; j <= m; j ++)
            {
                if(a[i][j] == 'H')
                    cur[i] += (1<<(m-j));//反着存状态
            }
        }
        for(int i = 1; i <= top; i ++)
        {
            num[i] = countnum(state[i]);
            if(fit(state[i], 1))//初始化第一行
            {
                dp[1][1][i] = num[i];
            }
        }
        for(int i = 2; i <= n; i ++)
        {
            for(int t = 1; t <= top; t ++)
            {
                if(! fit(state[t],i)) continue;
                for(int j = 1; j <= top; j ++)
                {
                    if(state[t]&state[j])continue;
                    for(int k = 1; k <= top; k ++)
                    {
                        if(state[t]&state[k])continue;
                        if(state[k]&state[j])continue;
                        dp[i][k][t]=max(dp[i][k][t],dp[i-1][j][k]+num[t]);
                    }
                }
            }
        }
        int ans = 0;
        for(int j = 1; j <= top; j ++)
            for(int k = 1; k <= top; k ++)
            ans = max(ans, dp[n][j][k]);
        printf("%d\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值