POJ 1185 炮兵阵地

POJ 1185 炮兵阵地

题目链接

Description

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

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

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

非常经典的状压 DP,和 HDUOJ 1565 这道题方法类似,只不过方格取数是不能相邻,这道题条件更复杂一些,要隔两格~
状压 dp 要暴力算出每行所有可能的取值情况,即 [ 0 , 2 m ) [0,2^m) [0,2m),又要错两格,也即可能取值情况的二进制中的所有 1 1 1 之间的间隔必须大于 2 2 2,这可以用 ( ( i & ( i > > 2 ) ) = 0 ) & & ( ( i & ( i > > 1 ) ) = 0 ) ((i\&(i>>2))=0)\&\&((i\&(i>>1))=0) ((i&(i>>2))=0)&&((i&(i>>1))=0) 来判断,通过上面的枚举和筛除就可以得到每一行的所有可能情况,下面考虑动态规划~
因为对第 i i i 行受第 i − 1 i-1 i1 行和第 i − 2 i-2 i2 行的影响,所以要比方格取数多一维,我们用 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 表示第 i i i 行以 j j j 为状态第 i − 1 i-1 i1 行以 k k k 为状态时的最大炮兵数量, v a l val val 表示第 i i i 行的炮兵数量,即 j j j 的二进制中 1 1 1 的个数,可以用内置函数 _ _ b u i l t i n _ p o p c o u n t \_\_builtin\_popcount __builtin_popcount 计算,则有状态转移方程:
d p [ i ] [ j ] [ k ] = m a x ( d p [ i ] [ j ] [ k ] , d p [ i − 1 ] [ k ] [ l ] + v a l ) , l & k = 0 , l & j = 0 dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+val),l\&k=0,l\&j=0 dp[i][j][k]=max(dp[i][j][k],dp[i1][k][l]+val),l&k=0,l&j=0
但此时复杂度还是太高,我们继续考虑优化,对于每一行的炮兵排布,我们可以用预处理成二进制,即有炮兵视作 1 1 1,否则为 0 0 0,存入数组 o p op op,那么对于每一行状态的选择 j j j,必须要满足 o p [ i ] & j ≠ 0 op[i]\&j\neq 0 op[i]&j=0,这样又筛除一部分进而能降低复杂度,对应的,状态转移时要满足:

o p [ i ] & j ≠ 0 op[i]\&j\neq 0 op[i]&j=0
o p [ i − 1 ] & k ≠ 0 op[i-1]\&k\neq 0 op[i1]&k=0
o p [ i − 2 ] & l ≠ 0 op[i-2]\&l\neq 0 op[i2]&l=0

在状态转移时可以不用考虑下标为负的情况,因为在C的语法里并不算错,AC代码如下:

#include
#include
using namespace std;
char s[105][1<<10];
int dp[105][100][100];
int n,m,cnt,ans,num[1<<10],op[1<<10];
int main(){
     
    scanf("%d%d",&n,&m);
    cnt=ans=0;
    for(int i=1;i<=n;i++) scanf("%s",s[i]);
    for(int i=0;i<(1<<m);i++) if(((i&(i>>2))==0)&&((i&(i>>1))==0)) num[++cnt]=i;
    for(int i=1;i<=n;i++){
     
        for(int j=0;j<m;j++) op[i]+=((s[i][j]=='H')<<j);
        for(int j=1;j<=cnt;j++){
     
            if(num[j]&op[i]) continue;
            for(int k=1;k<=cnt;k++){
     
                if(num[j]&num[k]) continue;
                int val=__builtin_popcount(num[j]);
                if(i>=2&&(num[k]&op[i-1])) continue;
                for(int l=1;l<=cnt;l++){
     
                    if(num[l]&op[i-2]) continue;
                    if((num[l]&num[k])==0&&(num[l]&num[j])==0) dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+val);
                }
                ans=max(dp[i][j][k],ans);
            }
        }
    }
    printf("%d\n",ans);
}

你可能感兴趣的:(状压DP,POJ,NOIP)