司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
https://www.luogu.org/problemnew/show/P2704
状态:f[i][j][k],表示第i行,当前行状态j,上一行状态k的最大数量。
如果枚举所有状态去dp,复杂度O(n*2^m*2^m*2^m)。
这时候我们就需要预处理出每一行有哪些状态是不会与题意(攻击范围左右各2,只考虑左右)冲突的,处理出s数组,共k个符合题意的,例如s[1]就表示第1个符合题意的状态。这样的话,当m=10的时候,每行的状态也最多只有60个左右。O(n*60^3)就可以接受了。g数组表示每种状态有几个1,即有几个炮车。
再预处理出前两行的状态,之后直接暴力转移即可。
首先有一个函数专门计算某一状态含有多少个1(即有多少个炮兵)
int get(int x)
{
int e=0;
while(x>0){
++e;
x-=x&(-x);
}
return e;
}
读入地图,将山地(不能放兵)的地方设为1 (状态中的1表示放炮兵的地方)
for(int i=1;i<=n;i++){
scanf("%s",ma);
for(int j=0;j<m;j++){
if(ma[j]=='H') map[i]+=1<<j;
}
}
预处理,判断出所有情况中可行的,对于每一行的任意两个1之间距离大于2
((j&(j<<2))==0)&&((j&(j>>2))==0)&&((j&(j<<1))==0)&&((j&(j>>1))==0)
给第1行赋初值
if((j&map[1])==0) f[1][0][k]=g[k];
给第2行赋初值,要判断第2行是否与第1行冲突
if(((s[i]&s[j])==0)&&((s[j]&map[2])==0)){
f[2][i][j]=max(f[2][i][j],f[1][0][i]+g[j]);
}
然后就是枚举行数i,枚举当前行状态j,枚举上一行状态p,枚举上两行状态q,同时判断j与当前行,j与p,p与q,j与q是否冲突,如果不冲突就更新dp
f[i][p][j]=max(f[i][p][j],f[i-1][q][p]+g[j]);
枚举两个状态,取最大值得出答案
for(int i=1;i<=k;i++)
for(int j=1;j<=k;j++)
ans=max(ans,f[n][i][j]);
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,k,ans;
char ma[15];
int map[101],s[1005],g[1005],f[102][1005][1005];
int get(int x)
{
int e=0;
while(x>0){
++e;
x-=x&(-x);
}
return e;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",ma);
for(int j=0;j<m;j++){
if(ma[j]=='H') map[i]+=1<<j;
}
}
int maxn=1<<m;
for(int j=0;j<maxn;j++){
if(((j&(j<<2))==0)&&((j&(j>>2))==0)&&((j&(j<<1))==0)&&((j&(j>>1))==0)){
k++;
s[k]=j;
g[k]=get(j);
if((j&map[1])==0){
f[1][0][k]=g[k];
}
}
}
for(int i=1;i<=k;i++){
for(int j=1;j<=k;j++){
if(((s[i]&s[j])==0)&&((s[j]&map[2])==0)){
f[2][i][j]=max(f[2][i][j],f[1][0][i]+g[j]);
}
}
}
for(int i=3;i<=n;i++){
for(int j=1;j<=k;j++){
if((s[j]&map[i])==0){
for(int p=1;p<=k;p++){
if((s[j]&s[p])==0){
for(int q=1;q<=k;q++){
if(((s[q]&s[j])==0)&&((s[q]&s[p])==0)){
f[i][p][j]=max(f[i][p][j],f[i-1][q][p]+g[j]);
}
}
}
}
}
}
}
for(int i=1;i<=k;i++){
for(int j=1;j<=k;j++){
ans=max(ans,f[n][i][j]);
}
}
printf("%d",ans);
return 0;
}