先看题;
很明显是状态压缩dp【因为本菜是在状态压缩dp专题里找到OVO】
先说一下本菜刚开始的想法吧;
本菜第一次对dp数组的定义是一个二维的;
f[i][j]表示状态第i行状态为j的最大炮兵数;
然后本菜的状态转移方程是这样的:
其中st[i].num第i行状态的炮兵数,当然,在状态转移之前要先判断状态是否满足题意;
但是...这个方法是错误的,为什么呢?因为我们这边是直接加上一个第i-1行状态的炮兵数,却没考虑他和i-2行上面的行数是否满足条件;
所以本菜在卡了一段时间【”一段“】后,想到【”想“】了新dp数组的定义,定义如下:
f[i][j][k]为第i行状态为j,第i-1行状态为k的最大值;
然后状态转移方程是这样的:
然后本菜发现,如果这样定义的话会爆空间,所以我们需要一个滚动数组优化,滚动数组优化的条件是一个状态只与之前计算出的状态有关,且这个状态在一个确定的区间内【比如上面这个只用到了i-2、i-1、i行的状态】;
下面就算我的详细注释了;
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int N=1<<10,M=101;
struct ch{
int num,st;//num为状态中有多少个炮车,st为状态对应的十进制数
};
char map[M][M];//存储地图
int n,m;
vector<vector<ch>> list;//list[i]里存储着所有i行合法的状态
ll f[3][N][N];//考虑第i行,且第i行的状态为j,第i-1行状态为k的最大值
ll ans;
bool jud(int st,int x){//状态为st,行 为x
for(int i=m;i>=1;i--){
int a=st%2;//a为状态在对应字符中的位置的状态,如果a=1什么这个位置上有炮车
if(a==1&&map[x][i]=='H') return 0;//如果在高地上有炮车,那么说明不符合条件
st/=2;
}
return 1;
}
void init(){
for(int i=1;i<=n;i++){//枚举每一行的状态
for(int k=0;k<1<<m;k++){
if(!jud(k,i)) continue;
if((!((k<<1)&k))&&(!((k<<2)&k))&&(!((k>>1)&k))&&(!((k>>2)&k))){//判断一行内是否有可以是否会相互攻击
int num=0,mid=k;//num存储k对应的状态有多少个炮车
while(mid){
num+=mid%2;
mid/=2;
}
list[i].push_back({num,k});
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>map[i][j];
}
}
if(n==1&&m==1&&map[n][m]=='P'){//这边要一个特判
printf("1"); exit(0);
}
list.resize(N);
init();
for(int i=0;i<list[1].size();i++){//特殊枚举第1行
f[1][list[1][i].st][0]=list[1][i].num;
}
for(int i=0;i<list[2].size();i++){//特殊枚举第2行
for(int j=0;j<list[1].size();j++){
if(list[1][j].st&list[2][i].st) continue;
f[2][list[2][i].st][list[1][j].st]=f[1][list[1][j].st][0]+list[2][i].num;
}
}
for(int i=3;i<=n;i++){
for(int j=0;j<list[i].size();j++){//枚举第i行的状态
for(int k=0;k<list[i-1].size();k++){//枚举第i-1行的状态
for(int z=0;z<list[i-2].size();z++){//枚举第i-2行的状态
//下面3行是判断i、i-1、i-2行是否有交集,即三行内的炮车是否可以互相攻击
if(list[i-2][z].st&list[i-1][k].st) continue;
if(list[i][j].st&list[i-2][z].st) continue;
if(list[i][j].st&list[i-1][k].st) continue;
//一个有点长的状态转移方程
f[i%3][list[i][j].st][list[i-1][k].st]=max(f[(i-1)%3][list[i-1][k].st][list[i-2][z].st]+list[i][j].num,f[i%3][list[i][j].st][list[i-1][k].st]);
}
}
}
}
for(int i=0;i<list[n].size();i++){
for(int j=0;j<list[n-1].size();j++){
if(list[n][i].st&list[n-1][j].st) continue;
ans=max(ans,f[n%3][list[n][i].st][list[n-1][j].st]);
}
}
cout<<ans;
}