[NOI2001] 炮兵阵地
题目描述
司令部的将军们打算在 N × M N\times M N×M 的网格地图上部署他们的炮兵部队。
一个 N × M N\times M N×M 的地图由 N N N 行 M M M 列组成,地图的每一格可能是山地(用 H \texttt{H} H 表示),也可能是平原(用 P \texttt{P} P 表示),如下图。
在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
输入格式
第一行包含两个由空格分割开的正整数,分别表示 N N N 和 M M M。
接下来的 N N N 行,每一行含有连续的 M M M 个字符,按顺序表示地图中每一行的数据。
输出格式
一行一个整数,表示最多能摆放的炮兵部队的数量。
样例 #1
样例输入 #1
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
样例输出 #1
6
提示
对于
100
%
100\%
100% 的数据,
1
≤
N
≤
100
1 \leq N\le 100
1≤N≤100,
1
≤
M
≤
10
1 \leq M\le 10
1≤M≤10,保证字符仅包含 P
与 H
。
—————————————————手动分割线——————————————
题解
状态设定
鉴于炮兵阵地每列间会互相影响,因此需要定义一个三维数组分别记录所在行数,本行状态和上行状态。设定dp[i][j][k]表示考虑前i行在第i行摆放方式为j,第i-1行摆放方式为k时的最大炮兵数量。
并不愉快的状态转移
定义一个数组sl[i]表示摆放方式为i时的炮兵数量(这一步在数据的预处理中完成),易得状态转移方程
- dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][p]+sl[j])
关于地形
同样的,我们可以使用二进制存储地形,再使用与运算就可以高效地排除与地形冲突的摆放方式
特别注意
- 当列与列间冲突时,dp[i][j][k]不应为0,可使用-1表示冲突(全部初始化成-1完美解决)
- 注意地形的赋值问题,若平原(P)为0则&摆放方式后数值为0表示摆放方式可行,若平原为1则&摆放方式后为地形的二进制数(摆放方式为地形的子集)表示摆放方式可行
代码区
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt;
int dp[110][70][70],sl[70],kx[1500],mp[110];
void inti()//预处理
{
for(int i=0;i<=(1<<m)-1;i++)
{
int j=i<<1,k=i<<2;
if((i&j)==0&&(i&k)==0) kx[cnt]=i,sl[cnt++]=__builtin_popcount(i);//防止同行冲突
}
cnt--;
}
int dx(string s)//处理地形
{
int ans=0;
for(int i=0;i<s.size();i++)
{
ans=ans<<1;
if(s[i]=='H') ans+=1;
}
return ans;
}
void out(int h)//高效的查错(迫真)
{
for(int i=0;i<=cnt;i++)
{
for(int j=0;j<=cnt;j++)
cout<<dp[h][i][j]<<" ";
cout<<endl;
}
cout<<"--------------------";
}
int main()
{
cin>>n>>m;
inti();
memset(dp,-1,sizeof(dp));
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
mp[i]=dx(s);
}
for(int j=0;j<=cnt;j++)//处理第一行
if((kx[j]&mp[1])==0) dp[1][j][0]=sl[j];
for(int j=0;j<=cnt;j++)//处理第二行
if((kx[j]&mp[2])==0)
for(int k=0;k<=cnt;k++)
if(dp[1][k][0]>=0&&(kx[j]&kx[k])==0) dp[2][j][k]=max(dp[2][j][k],dp[1][k][0]+sl[j]);
for(int i=3;i<=n;i++)//第i行
for(int j=0;j<=cnt;j++)//往第i行放入状态可行j
if((kx[j]&mp[i])==0)//确保准备要放的状态kx[j]与地形不冲突
for(int k=0;k<=cnt;k++)//枚举第i-1行的状态
for(int p=0;p<=cnt;p++)//枚举第i-2行的状态
if(dp[i-1][k][p]>=0&&(kx[k]&kx[j])==0&&(kx[j]&kx[p])==0)
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][p]+sl[j]);
int mx=0;
for(int i=0;i<=cnt;i++)
for(int j=0;j<=cnt;j++)
mx=max(mx,dp[n][i][j]);
cout<<mx;
return 0;
}