T01185: 炮兵阵地
状压dp, http://cs101.openjudge.cn/practice/01185/
司令部的将军们打算在NM的网格地图上部署他们的炮兵部队。一个NM的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
输入
第一行包含两个由空格分割开的正整数,分别表示N和M; 接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
输出
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
样例输入
5 4
PHPP
PPHH
PPPP
PHPP
PHHP样例输出
6来源
Noi 01
【陈子良 25物理学院】这题与30191互不侵犯是类似的,只是情况更复杂一些。首先,由于山的存在,不同行的可能状态是不一样的;其次,当前可能的状态与前面两行的状态有关。
我们用函数state(i)生成矩阵第i行的单行可能状态a。它有两个要求:一个是炮兵间距要大于2,即a&(a<<1)==0 and a&(a>>1)==0 and a&(a<<2)==0 and a&(a>>2)==0;另一个是炮兵不能放在山上,我们用s表示矩阵第i行的山的位置,s的二进制中第j位为1表示第i行第j列为山,合法状态要求a&s==0。
接下来滚动地推导前n行能放置的最多的炮兵数量。我们记state0为当前行可能状态集合,state1为上一行可能状态集合,state2为上两行可能状态集合。用dp[n][i][j]表示考虑到矩阵的前n行,当前行状态为a=state0[i],上一行状态为b=state1[j]时的最大炮兵数量,其中维度n是滚动的。如果a与b冲突,即a&b!=0,直接记为0。如果a与b不冲突,遍历上两行可能状态c=state2[k],如果a,b,c不冲突,即a&b==a&c==b&c==0,那么选取其中炮兵数量最多的状态,即max(dp[n-1][j][k]),再加上a中含有的炮兵数num(a)就是dp[n][i][j]的值。
对于状压dp类题,对可能的状态进行提前筛选并存储到state中,这一步是十分必要的,否则会TLE。但这也导致对系统状态的描述变得非常抽象,一定要仔细思考、多加体会。
N,M=map(int,input().split())
grid=[]
for _ in range(N):
grid.append(list(input()))
# 状态a中的炮兵数
def num(a):
return bin(a).count('1')
# 生成第i行所有合法的单行状态
def state(i):
l=grid[i][:]
x=0
s=0
while l:
if l.pop()=='H':
s+=2**x
x+=1
l1=[]
for a in range(1<<M):
if a&(a<<1) or a&(a>>1) or a&(a<<2) or a&(a>>2):
continue
if not a&s:
l1.append(a)
return l1
# N=1情形特判
if N==1:
state0=state(0)
print(max([num(a) for a in state0]))
exit()
# 初始化
state2=state(0) # 上两行状态
state1=state(1) # 上一行状态
dp=[[0]*len(state2) for _ in range(len(state1))]
for i in range(len(state1)):
for j in range(len(state2)):
b=state1[i]
c=state2[j]
if not b&c:
dp[i][j]=num(b)+num(c)
# dp的n方向维度是滚动的
for n in range(2,N):
state0=state(n) # 当前行状态
dp1=[[0]*len(state1) for _ in range(len(state0))]
for i in range(len(state0)):
a=state0[i]
m=num(a)
for j in range(len(state1)):
b=state1[j]
if a&b:
continue
for k in range(len(state2)):
c=state2[k]
if not a&c and not b&c:
dp1[i][j]=max(dp1[i][j],m+dp[j][k])
dp=[row[:] for row in dp1]
state2=state1[:]
state1=state0[:]
print(max([max(row) for row in dp]))