其他分享
首页 > 其他分享> > Luogu P2704 [NOI2001]炮兵阵地

Luogu P2704 [NOI2001]炮兵阵地

作者:互联网

题目描述

司令部的将军们打算在\(N\times M\)的网格地图上部署他们的炮兵部队。一个\(N\times M\)的地图由\(N\)行\(M\)列组成,地图的每一格可能是山地(用“\(H\)” 表示),也可能是平原(用“\(P\)”表示),如下图。

在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围

不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内

最多能够摆放多少我军的炮兵部队。

输入格式

第一行包含两个由空格分割开的正整数,分别表示\(N\)和\(M\);

接下来的\(N\)行,每一行含有连续的\(M\)个字符(‘\(P\)’或者‘\(H\)’),中间没有空格。按顺序表示地图中每一行的数据。\(N\leq 100, M\leq 10\) 。

输出格式

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

样例

输入:               输出: 6
5 4
PHPP
PPHH
PPPP
PHPP
PHHP

思路

这是一道相当DL的状压DP题,至少我是想了写了好久。

这个题的DL之处就在于炮兵的攻击范围是个“十”字型的,这就需要我们多维护一维状态。

设\(f[i][j][k]\)表示当前在第\(i\)行,第\(i\)行的状态是\(j\),并且第\(i-1\)行的状态是\(k\) 。

在读入的过程中,我们先使用一个数组(一维,状压),记录山地和平原的分布(山地为\(1\),平原为\(0\))。

然后我们在使用一个数组(仍然是一维、状压),记录每一个状态中各有多少个\(1\) 。

接下来就是预处理部分。预处理的是\(f[0][i][0]\)的情况(边界)。此时就涉及到判断左右两格是否能放的情况。这就涉及到位运算中的左移操作。我们把原集合左移\(1\)或\(2\),然后再和原集合进行按位与的运算,若结果不为\(0\),那说明一定有不合法的情况,舍去。还有就是判断是否为山地,和上述方法类似,不再赘述。

由于炮兵的攻击范围是上下左右各两个格,所以在开始DP之前需要先预处理出前两行的情况。然后就是从上到下枚举每一行进行DP即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define debug puts("wtf");
int n, m;
int f[3][1 << 10][1 << 10];
int sum[1 << 10], a[15];
char ip[15];
inline int read(void){
    int f = 1, x = 0;char ch;
    do{ch = getchar();if(ch=='-')f = -1;} while (ch < '0' || ch > '9');
    do{ x = x * 10 + ch - '0';ch = getchar();} while (ch >= '0' && ch <= '9');
    return f * x;
}
inline int _max(int x, int y) { return x > y ? x : y; }
inline int get_sum(int k){
    int tot = 0;
    while(k){
        if((k&1)==1) ++tot;
        k >>= 1;
    }
    return tot;
}
int main(){
    n = read(), m = read();
    for (int i = 0; i < n;++i){
        scanf("%s", ip);
        for (int j = 0; j < m;++j){
            a[i] <<= 1;
            a[i] += (ip[j] == 'P' ? 0 : 1);//判断是否为山地,山地为1,平原为0
        }
    }
    for (int i = 0; i < (1 << m);++i)
        sum[i] = get_sum(i);//求出每种情况中1的位数
    for (int i = 0; i < (1 << m);++i){
        if((i&(i<<1))||(i&(i<<2))||(i&a[0])) continue;//判断是否满足条件
        f[0][i][0] = sum[i];//赋值
    }
    for (int i = 0; i < (1 << m);++i){
        for (int j = 0; j < (1 << m);++j){
            if((i&(i<<1))||(i&(i<<2))||(j&(j<<1))||(j&(j<<2))||(i&j)||(i&a[1])||(j&a[0]))
                continue;//判断是否满足条件
            f[1][i][j] = sum[i] + sum[j];//赋值
        }
    }
    for (int i = 2; i < n;++i){//挨行枚举
        for (int j = 0; j < (1 << m);++j){//枚举当期行的情况
            if((j&(j<<1))||(j&(j<<2))||(j&a[i])) continue;//不合法的舍去
            for (int k = 0; k < (1 << m);++k){//枚举上一行的情况
                if((k&(k<<1))||(k&(k<<2))||(k&a[i-1])||(j&k)) continue;//不合法的舍去
                for (int s = 0; s < (1 << m);++s){//枚举上上行的情况
                    if((s&(s<<1))||(s&(s<<2))||(s&a[i-2])||(j&s)||(k&s)) continue;
                    f[i % 3][j][k] = _max(f[i % 3][j][k], f[(i - 1) % 3][k][s] + sum[j]);//由于空间问题,而且我们只需要维护3行的信息,所以这里使用滚动数组
                }
            }
        }
    }
    int ans = -1;
    for (int i = 0; i < (1 << m);++i){
        for (int j = 0; j < (1 << m);++j){
            ans = _max(ans, f[(n - 1) % 3][i][j]);//考虑每种情况,取max即可
        }
    }
    printf("%d\n", ans);
    return 0;
}

标签:ch,炮兵部队,int,Luogu,地图,攻击,NOI2001,P2704,include
来源: https://www.cnblogs.com/ShadowFlowhyc/p/13379372.html