题面
题目描述
司令部的将军们打算在NM的网格地图上部署他们的炮兵部队。一个NM的地图由N行M列组成,地图的每一格可能是山地(用”H” 表示),也可能是平原(用”P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
输入格式
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P’或者’H’),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。
输出格式
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
题解
很明显,一个炮兵部队的摆放受到前两行的制约。
状态:
f[i][j][k]
f
[
i
]
[
j
]
[
k
]
代表第i行状态是j,第i-1行状态是k的答案。
我们令
count[i]
c
o
u
n
t
[
i
]
是二进制数i的1的个数。
valid[i][j]
v
a
l
i
d
[
i
]
[
j
]
代表状态j是否能在第i行出现。
所以状态转移方程:
如果
valid[i][j]且valid[i−1][k],并且j|k==j|l==0
v
a
l
i
d
[
i
]
[
j
]
且
v
a
l
i
d
[
i
−
1
]
[
k
]
,
并
且
j
|
k
==
j
|
l
==
0
,那么有
f[i][j][k]=max{f[i−1][k][l]}+count[j]
f
[
i
]
[
j
]
[
k
]
=
max
{
f
[
i
−
1
]
[
k
]
[
l
]
}
+
c
o
u
n
t
[
j
]
## code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
const int maxn=200;
int n,m,dp[maxn][maxn][maxn];
int a[maxn];
int top,s[maxn],num[maxn];
void init(){
n=read();m=read();
int k=1<<m;
for(int i=0;i<k;i++)
if((i&(i<<1))==0&&(i&(i<<2))==0){
s[top]=i;
num[top]=0;
int j=i;
while(j)
{
num[top]+=j%2;
j/=2;
}
top++;
}
string s;
for(int i=0;i<n;i++){
a[i]=0;
cin>>s;
for(int j=0;j<m;j++)
if (s[j]=='P')
a[i]+=1<<j;
}
}
void one_and_two(){//前两行的dp值需要预先处理,否则无法转移。
for(int j=0;j<top;j++)
{
if((s[j]&a[0])!=s[j])
continue;
for(int k=0;k<top;k++)
dp[0][j][k]=num[j];
}
if(n>1)
for(int j=0;j<top;j++)
{
if((s[j]&a[1])!=s[j])
continue;
for(int k=0;k<top;k++)
{
if((s[j]&s[k])==0)
dp[1][j][k]=dp[0][k][0]+num[j];
}
}
}
void work(){
for (int i=2;i<n;i++)
{
for(int j=0;j<top;j++)
{
if((s[j]&a[i])!=s[j])
continue;
for(int k=0;k<top;k++)
{
if(s[j]&s[k])
continue;
for(int h=0;h<top;h++)
{
if (s[j]&s[h])
continue;
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][h]);
}
dp[i][j][k]+=num[j];
}
}
}
int ans=0;
for(int j=0;j<top;j++)
{
for(int k=0;k<top;k++)
ans=max(ans,dp[n-1][j][k]);
}
printf("%d\n",ans);
}
int main(){
init();
one_and_two();
work();
return 0;
}