[luogu p 2704] 炮兵阵地 (状压dp)

这题我交了3遍没过,于是我抄了题解。原题解不是很详细于是乎我进行了一些注释。

注释很详细!!!

注释很详细!!!

注释很详细!!!

若是初学建议先做 luogu p1879 [USACO06NOV]玉米田Corn Fields:https://www.luogu.org/problemnew/show/P1879

                                【POJ – 3311 】Hie with the Pie:https://vjudge.net/problem/POJ-3311

题目链接: https://www.luogu.org/problemnew/show/P2704

题目描述

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

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例#1:  

5 4
PHPP
PPHH
PPPP
PHPP
PHHP

输出样例#1: 

6
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cctype>

using namespace std;

char p[103];
int n,m,k,ans,map[103],s[1005],g[1005];
int f[102][1005][1005];//f[i][j][k]表示第i行的状态为k上一行的状态为j时能放置的炮兵的最大值。 

int get(int x)//用于计算有多少炮兵(即有多少1) 。 
{
    int e = 0;
    while(x > 0)
    {
        e++;
        x -= x & (-x);/*x减去-x的二进制与x的二进制按位与正好可以去掉x得最小位上的1,
所以去除几次才能使x=0就是有几个1。*/
    }
    return e;
}

int main( )
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%s",p);
        for(int j = 0; j < m; ++j)
            if(p[j] == 'H')	map[i] += 1<<j;//如果地图为山地就赋值为1。 
    } 
    for(int i = 0; i <= (1<<m)-1; i++)
        if((( i & (i<<1)) == 0 ) && (( i & (i<<2)) ==0 ) && (( i & (i>>1)) == 0) &&((i & (i>>2)) == 0))
        //不考虑地图,判断每个1左右2格内是否为1,即判断该状态是否存在。 
        {
            k++;//每有一种状态存在k++。k即为所有存在的状态数。 
            s[k] = i;//保存每种可能状态i 。 
            g[k] = get(i);//计算该状态i有多少炮兵 。 
            if((i & map[1]) == 0)	f[1][0][k] = g[k];
        //因为山地被赋值成1,所以若状态i与第一行地图在同一位上没有相同的1,该状态就存在。 
        }
    for(int i = 1; i <= k; i++)
        for(int j = 1; j <= k; j++)
            if(((s[i] & s[j]) == 0) && ((s[j] & map[2]) == 0))
        //如果状态i和状态j没有1在同一列,并且状态j和地图不冲突,就存到的第二行 。 
                f[2][i][j] = max(f[2][i][j], f[1][0][i] + g[j]);//求第二行的最大值。 
    for(int i = 3; i <= n; i++)//求剩余行数 
        for(int j = 1; j <= k; j++)
            if((map[i] & s[j]) == 0)//如果地图第i行与当前状态j不冲突 
                for(int o = 1; o <= k; o++)//枚举i-1行状态 
                    if((s[o] & s[j]) == 0)//如果前i-1行状态o与第i行状态j不冲突 
                        for(int q = 1; q <= k; q++)//枚举i-2行状态 
                            if(((s[q] & s[o]) == 0) && ((s[q] & s[j]) == 0))
                            //如果当前枚举的第i行与第i-1行和第i-2行不冲突 
                                f[i][o][j] = max(f[i][o][j] , f[i-1][q][o]+g[j]);//求当前状态的最大值 
    for(int i = 1; i <= k; i++)
        for(int j = 1; j <= k; j++)
            ans = max(f[n][i][j],ans);//比较计算到第n行时的最大值 
    cout<<ans;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jvruo233/article/details/82559813