1. 问题描述:

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

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

输入描述:
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(P或者H),中间没有空格。按顺序表示地图中每一行的数据。
输出描述:
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
示例1
输入
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
输出
6
备注:
N <=100,M <=10 N≤100, M≤10。

链接:https://ac.nowcoder.com/acm/problem/50526
来源:牛客网

2. 思路分析:

① 这一道题目属于状态压缩dp入门的经典题目,状态压缩一般使用二进制0和1来表示对应的状态,可以将二进制对应的状态映射为一个十进制数字,这样一个十进制数字就可以表示对应的状态。当前位置是否可以放置炮兵部队受到i - 1与i - 2行的影响,并且需要受到同一行的影响(同一行有可能连续的两个位置或者三个位置放置了炮兵)。我们可以先处理一下每一行可能的状态,因为比如放置连续的炮兵的这些情况是不符合题目条件的我们要做的是先排除掉那些不符合题目要求的状态,将每一行可能的状态加入到state列表中,state存储的状态中使用1表示当前这一行的这个位置放置了炮兵,0表示当前这一行的这个位置没有放置炮兵,其中当每一个位置都表示1的时候对应的十进制数字是最大的,所以我们可以预处理一下每一行可能的状态(预处理之后状态去掉很多不满足题目的状态),因为炮兵的位置对于上下左右四个方向攻击的位置都是从当前位置开始移动两格,所以我们可以使用i & (i << 1)与i & (i << 2)判断是否有连续的两个1或者三个1相邻(想与操作的结果为1表示当前位置放置了炮兵这两种情况是不允许的),因为移位操作之后两个数字的连续的1是错位的,如果结果为1说明是存在连续的两个1或者三个1相邻,我们可以在0~1 << m(m为列数,表示2 ^ m)的范围中枚举这些状态,只要是i & (i << 1) == 0 and i & (i << 2) == 0表示当前的状态是可能的状态并将其加入到state列表中,并且在枚举可能状态的时候计算出每一个状态中包含的1的数目也就是可能放置的炮兵数目将其存储到列表num中,这样在后面就可以直接使用了。我们在输入每一行平地或者是山地的字符串时记录下山地的位置,也就是将每一行对应的字符H对应的位置标记为1,将每一行山地的位置通过二进制对应的十进制数字表示存储到列表map1中,这样后面在判断放置炮兵是否可以放置的时候就非常方便了。

② 接下来是dp部分的内容。先预处理第一行,将可能的状态存储到三维dp列表中,dp[i][j][k]表示的意思是第i行状态为j,i - 1行状态为k的时候炮兵的数目。第一行可以放置任意的状态,计算出对应状态的二进制中1的数目初始化到dp列表中表示当前状态的炮兵数目。接下来是dp状态的转移,我们的目的是对于当前的这一行尝试所有可能的状态,判断当前的状态是否与之前的i - 1与i - 2行状态发生冲突,所以需要使用四重for循环解决,第一重循环表示当前是第i行,第二重循环表示当前这一行的状态为j,第三层与第四层循环表示i - 1行与i - 2行可能的状态,我们在枚举i,i-1,i-2行可能状态的时候需要判断当前状态是否有的位置放在了山地位置,使用state[j] & map1[i],并且需要判断i,i-1,i-2行之间是否存在冲突,使用state[j] & state[k],state[t] & state[j]...相与操作表示两行的对应列位置是否存在相同的1如果相同表示是冲突的。

③ 三维dp我们可以这样理解,尝试每一行与i - 1与i-2行可能的状态,初始化第一行所有可能的状态,当我们填好了前一行之后那么当前这一行的可能的状态就确定了(对照题目中的图会更好理解冲突),其实可以理解为所有可能的状态的组合(暴力枚举的优化),我们只需要记录下当前这一行与上一行可能的状态即可,也即使用三维列表即可解决。通过多重循环一直递推到最后一行可能的状态,最后求解出第n行可能的状态的最大值即为答案。

3. 代码如下:

网上找的比较好理解的c++代码:

//num[j]为第j个状态中的二进制中的1的个数
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
#define MAX 105
int map[MAX];//存放二进制表示每行地形的限制
char s[MAX];//表示输入的串
int dp[MAX][MAX][MAX];
int sta[MAX];
int num[MAX];
int n,m;
void chu()
{memset(dp,-1,sizeof(dp));memset(sta,0,sizeof(sta));memset(num,0,sizeof(num));memset(map,0,sizeof(map));
}
int count(int x)
{int lala=0;while(x){if(x&1)lala++;x>>=1;//此处注意}return lala;
}
int main()
{while(~scanf("%d%d",&n,&m)){chu();for(int i=1;i<=n;i++){scanf("%s",s);for(int j=0;j<m;j++){if(s[j]=='H')map[i]+=(1<<j);//记录下山地的位置}}int cnt=0;for(int i=0;i<(1<<m);i++){if(!(i&(i<<2))&&!(i&(i<<1)))//如果此行互不影响{num[cnt]=count(i);sta[cnt++]=i;//先预处理,保存一下状态}}for(int i=0;i<cnt;i++){if(!(sta[i]&map[1]))dp[1][i][0]=num[i];//第一行所有状态赋初值}for(int i=2;i<=n;i++){for(int j=0;j<cnt;j++)//第i行j个状态{if(sta[j]&map[i])//只要不等于0,就说明第i行与j行地形冲突.冲突直接跳过continue;for(int k=0;k<cnt;k++)//第i-1行的第k个状态{if(sta[k]&map[i-1])//状态与地形冲突continue;if(sta[j]&sta[k])//第i-1行与i-2行相互攻击continue;for(int t=0;t<cnt;t++){if(sta[t]&map[i-2])//与地形冲突continue;if(sta[t]&sta[k])continue;//第i-2行和i-2行攻击if(sta[t]&sta[j])//i-2行与i-1行攻击continue;dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);}}}}int ans=0;for(int i=0;i<cnt;i++){for(int j=0;j<cnt;j++){ans=max(ans,dp[n][i][j]);}}cout<<ans<<endl;}return 0;
}

自己改写的python代码(超时):

# 计算当前的n对应的二进制数字有多少个1
def count(n: int):cnt = 0while n:if n & 1:cnt += 1n >>= 1return cnt# dp[i][j][k]表示第i行是j状态,i-1行是k状态
if __name__ == '__main__':n, m = map(int, input().split())map1 = [0] * 105for i in range(1, n + 1):s = input()for j in range(len(s)):# 在每一行计算出山地的位置将其映射到二进制状态中后面可以使用与state中的状态与山地状态相与看是否为为0部位0说明# 放置的炮兵位置有的是在山地中的if s[j] == "H":map1[i] += (1 << j)# dp[i][j][k]表示第i行状态为j, i - 1行状态为k的时候放置的炮兵数目dp = [[[-1] * 105 for i in range(105)] for j in range(105)]status = [0] * 105num = [0] * 105cnt = 0# 预处理m为列数通过移位操作判断是否存在连续的两个或者三个炮兵放在一起for i in range(1 << m):if (not (i & (i << 2))) and (not (i & (i << 1))):# 计算当前状态对应的炮兵数量num[cnt] = count(i)status[cnt] = icnt += 1# 初始化第一行for i in range(cnt):# 不是山地if not (status[i] & map1[1]):dp[1][i][0] = num[i]for i in range(2, n + 1):for j in range(cnt):# 当前状态中有些位置在山地中放了炮兵, continueif status[j] & map1[i]: continue# i-1行状态为kfor k in range(cnt):# i-1行有些位置在山地中放了炮兵, continueif status[k] & map1[i - 1]: continue# i行与i-1行存在了列位置上的冲突, &表示有的位置为1if status[k] & status[j]: continuefor t in range(cnt):if status[t] & map1[i - 2]: continue# i-2行与i与i-1行发生冲突continueif status[t] & status[j]: continueif status[t] & status[k]: continue# 加上当前这一行的炮兵数目dp[i][j][k] = max(dp[i][j][k], dp[i - 1][k][t] + num[j])res = 0# 求解第n行中所有状态的最大值for i in range(cnt):for j in range(cnt):res = max(res, dp[n][i][j])print(res)

炮兵阵地(状压dp)相关推荐

  1. POJ - 1185 炮兵阵地(状压dp)

    题目链接:点击查看 题目大意:中文题,题意很清晰,不多赘述 题目分析:最基础的状压dp,需要考虑如何转移,因为每一个炸弹所涉及的范围都是上下左右两个格子,我们可以从第一行开始向下转移,这样某一行的状态 ...

  2. 洛谷P2704 [NOI2001]炮兵阵地(状压dp)

    题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P&quo ...

  3. POJ 1185 炮兵阵地 (状压DP)

    炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 14869   Accepted: 5575 Description ...

  4. POJ1185 炮兵阵地 状压DP

    炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 31819   Accepted: 12295 Descriptio ...

  5. P2704 炮兵阵地 (状压dp入门题) 题解

    题意简述:给定一个大小为n*m的棋盘,棋盘上'H'表示不可放置,'P'表示可放置,一个棋子在棋盘上的攻击范围是其左右上下2格以内,求不冲突情况下的最多摆放棋子数. 分析:决策是对于一个P格子考虑放或不 ...

  6. POJ 1185 炮兵阵地 状压dp

    http://poj.org/problem?id=1185 经典题目不必多说,直接贴代码. 1 #include<cstdio> 2 #include<cstring> 3 ...

  7. 状压dp之二之三 炮兵阵地/玉米田 By cellur925

    一.简单的状压dp 玉米田 题目描述 Farmer John has purchased a lush new rectangular pasture composed of M by N (1 ≤ ...

  8. NYOJ 81:炮兵阵地(状压DP)

    炮兵阵地 时间限制:2000 ms  |  内存限制:65535 KB 难度:6 描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地 ...

  9. 刷题周记(九)——#状压DP:最短Hamilton路径、小国王(互不侵犯)、玉米田(Corn Fields G)、愤怒的小鸟、吃奶酪、炮兵阵地、宝藏 #区间DP:清空字符串#DP:关灯问题II

    文章目录 --2020年12月20日(周日)------------------ 状压DP 一.最短Hamilton路径(模板题) 二.玉米田(P1879 [USACO06NOV]Corn Field ...

  10. POJ 1185 炮兵阵地 【状压DP】

    <题目链接> 题目大意: 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平 ...

最新文章

  1. 工业机器人演示码垛和卸垛_浅谈饲料自动码垛机的应用及正确操作方法
  2. java提高篇(三十)-----Iterator
  3. react 动态修改路由_reactjs – 如何动态添加重定向到反应路由器?
  4. vim多窗口使用技巧
  5. 使用UltraEdit来拷贝粘贴二进制
  6. 使用Blink SQL+UDAF实现差值聚合计算
  7. 中文代码示例之Vuejs入门教程(一)
  8. 新型开关电源优化设计与实例详解全书.pdf_高频电路设计中,如何应对“不理想”的电容与电感?...
  9. 服务器装系统怎样进bios设置,重装系统怎么进入bios设置界面?进入bios设置界面详细教程!...
  10. K8S异常 sonar status is ‘PENDING’
  11. FAT16 FAT32 文件系统
  12. 华为手机相册怎么镜像翻转_手机相册里的最近删除怎么关
  13. android pdf修改字体大小,如何编辑pdf文字(安卓上最好的pdf笔记app)
  14. amcharts的使用
  15. 201509281125_《为什么移动app会很慢的深度分析(摘自司徒正美博客园文章)》
  16. xtu oj 1375斐波纳契
  17. 5G智慧工业 | PLC设备远程监控系统解决方案
  18. IC 产品的质量与可靠性测试
  19. 人生低谷一日感悟+收获
  20. 2020-03-05

热门文章

  1. mysql secure_file_priv 属性相关的文件读写权限问题
  2. 放弃去FBI的机会,在美做了2年数据研究工作,如今回国创业,他把全球最顶级的智能预测算法带到了国内
  3. 六款强大的开源数据挖掘工具
  4. table表格标签css固定最后一列方案
  5. 【原创】在winform程序中实现在IE浏览器中打开一个新的页面,全屏化并屏蔽IE窗口的工具栏和地址栏
  6. Windows10 无法使用内置管理员打开照片问题解决
  7. Modifier在java_Java-Modifier类常用方法详解
  8. 深圳摇号验证码一输完就变的解决办法
  9. 使用高德地图api在页面中展示地图
  10. 投影仪与电视的C位之争,电视会成为下一个被淘汰的电器吗?