时空限制 1000ms / 128MB
题目描述
司令部的将军们打算在NM的网格地图上部署他们的炮兵部队。一个NM的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
输入格式:
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符(‘P’或者‘H’),中间没有空格。按顺序表示地图中每一行的数据。N≤100;M≤10。
输出格式:
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
题目分析
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示考虑前
i
i
i行,且第
i
i
i行状态为
j
j
j,第
i
−
1
i-1
i−1行状态为
k
k
k能放置的最多数量
dp方程
d
p
[
i
]
[
j
]
[
k
]
=
m
a
x
(
d
p
[
i
−
1
]
[
k
]
[
l
]
+
s
u
m
(
j
)
)
dp[i][j][k]=max(dp[i-1][k][l]+sum(j))
dp[i][j][k]=max(dp[i−1][k][l]+sum(j))(状态
j
,
k
,
l
j,k,l
j,k,l都合法,
s
u
m
(
j
)
sum(j)
sum(j)为
j
j
j状态的炮兵数)
如果直接枚举状态更新复杂度为
O
(
n
∗
(
2
m
)
3
)
O(n*(2^m)^3)
O(n∗(2m)3),显然无法承受
于是考虑先把每一行只考虑单行不考虑地形的所有和合法状态枚举出来(即只考虑一行内不互相攻击)
可以通过计算得出一行内这样的合法状态数不超过70
于是枚举状态的时间复杂度就大大降低
最后答案为 a n s = m a x ( d p [ n ] [ i ] [ j ] ) ans=max(dp[n][i][j]) ans=max(dp[n][i][j])
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
#define lowbit(x) ((x)&(-x))
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int N=110;
const int M=13;
int n,m;
int rem[N];
int st[1<<M],num[1<<M],cnt;
int dp[N][M*10][M*10],ans;
char ss[M];
int check(int x)
{
if(((x<<1)&x)||((x>>1)&x)) return 0;
if(((x<<2)&x)||((x>>2)&x)) return 0;
return 1;
}
int qsum(int x)
{
int res=0;
for(int i=x;i>0;i-=lowbit(i)) res++;//用lowbit计算1的个数
//for(int i=1;i<=15;++i)
//if(x&(1<<i-1)) res++;
return res;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)
{
scanf("%s",&ss);
for(int j=0;j<m;++j)
if(ss[j]=='H') rem[i]|=1<<j;
}
for(int i=0;i<=(1<<m)-1;++i)//枚举一行内不考虑地形的合法状态
if(check(i)){
st[++cnt]=i;
num[cnt]=qsum(i);
}
memset(dp,-1,sizeof(dp));
for(int i=1;i<=cnt;++i)
if(!(st[i]&rem[1]))
dp[1][i][1]=num[i];//初始化第1行状态为i,上一行状态为全0
for(int i=2;i<=n;++i)
for(int j=1;j<=cnt;++j)//第i行的状态
if(!(st[j]&rem[i]))
for(int k=1;k<=cnt;++k)//第i-1行的状态
if(!(st[k]&rem[i-1])&&!(st[j]&st[k]))
for(int l=1;l<=cnt;++l)//第i-2行的状态
if(!(st[j]&st[l])&&!(st[l]&st[k])&&!(rem[i-2]&st[l])&&dp[i-1][k][l]!=-1)
dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num[j]);
for(int i=1;i<=cnt;++i)
for(int j=1;j<=cnt;++j)
ans=max(ans,dp[n][i][j]);
printf("%d",ans);
return 0;
}