poj 1185 炮兵阵地(DP-状态DP)

炮兵阵地
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 19226 Accepted: 7424

Description

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。 

Input

第一行包含两个由空格分割开的正整数,分别表示N和M; 
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。

Output

仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

Sample Input

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

解题思路:每一行只跟上一行和上上一行有关系,而M<=10,因此每一行的状态不多,用程序枚举一下只有60种。所以,我们先预处理这60种状态,0表示未放,1表示已放。每一行用二进制表示,P用0表示可以放但是还没被放,H用1表示不可以放。

dp[k][sta1][sta2]表示当考虑第k行时,k-1行的是第sta1中状态,k-2行是第sta2中状态,之后所能放的最大值。

我们枚举在第k行的状态,当((way[sta1]|way[sta2]|mp[k])&way[i]) <= 0 时way[i]可以作为第k行的状态,way是预处理的60种状态,mp为读入的状态。

例如:设k-1行为01101,k-2行为01001,mp[k]为01010

01101

01001

-----

01101

表示第1、4位可以放,第2、3、5位不可以放,然后再跟mp[k]取或,mp[k]中0表示P,1表示H

01101

01010

-----

01111

则表示只有第1位可以放,然后再和way[i]取与,way[i]中的1表示第k行在该位放,0表示不放。

如果该位可以放而且第k行在该位放了,则0&1 == 0,

如果该位可以放而且第k行在该位不放,则0&0 == 0,

如果该位不可以放而第k行在该位放了,则1&1 == 1,

如果该位不可以放而第k行在该位不放,则1&0 == 0.

因此结果<=0表示第k行可以采用way[i]这个状态。

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

const int maxn = 105;
int dp[maxn][maxn][maxn] , v[maxn][maxn][maxn] , vv;
int way[maxn] , sum[maxn];
int mp[maxn] , N , M;
int cnt;
int vis[11];

void init(){
    for(int i = 0; i < maxn; i++){
        for(int j = 0; j < maxn; j++){
            for(int k = 0; k < maxn; k++) dp[i][j][k] = 0 , v[i][j][k] = 0;
        }
    }
    vv = 1;
}

void initial(){
    memset(way , 0 , sizeof way);
    memset(sum , 0 , sizeof sum);
    memset(mp , 0 , sizeof mp);
    memset(vis , 0 , sizeof vis);
    cnt = 0;
}

void readcase(){
    string str;
    for(int i = 0; i < N; i++){
        cin >> str;
        int sta = 0;
        for(int j = M-1; j >= 0; j--){
            if(str[j] == 'H'){
                sta += (1<<j);
            }
        }
        mp[i] = sta;
    }
}

void dfs(int i , int sta , int s){
    if(i >= M){
        way[cnt] = sta;
        sum[cnt++] = s;
    }else{
        int flag = 1;
        for(int k = 1; k <= 2; k++){
            if(i-k >= 0 && vis[i-k] != 0){
                flag = 0;
                break;
            }
        }
        dfs(i+1 , sta , s);
        if(flag){
            vis[i] = 1;
            dfs(i+1 , sta+(1<<i) , s+1);
            vis[i] = 0;
        }
    }
}

int DP(int k , int sta1 , int sta2){
    if(k >= N) return 0;
    if(v[k][sta1][sta2] == vv) return dp[k][sta1][sta2];
    int ans = 0;
    v[k][sta1][sta2] = vv;
    for(int i = 0; i < cnt; i++){
        if(((way[sta1]|way[sta2]|mp[k])&way[i]) <= 0){
            ans = max(ans , DP(k+1 , i , sta1)+sum[i]);
        }
    }
    return dp[k][sta1][sta2] = ans;
}

void computing(){
    dfs(0 , 0 , 0);
    printf("%d\n" , DP(0 , 0 , 0));
    vv++;
}

int main(){
    init();
    while(~scanf("%d%d" , &N , &M)){
        initial();
        readcase();
        computing();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值