互不侵犯KING
题意:
给一个$n * m$大小的地图, H表示山地,P代表平原,你可以在平原上部署一支炮兵部队,他们的攻击距离如图所示
让你求最多可以部署多少支部队,使得任意两支部队之间无法互相攻击
思路:
状态压缩DP,我们把每一行的状态用二进制来表示,山地为1,平原为0;然后我们枚举每一行的状态,把符合左右两格互不影响的所有状态存入$st$数组当中,这一状态S中的炮兵的数量我们用num[cnt]=__builtin_popcount(s);找出其中的1的个数即是。然后我们逐行遍历,枚举所有状态,合理的就计算
状态设置:
我们设置$ f[i][j][k]$ 表示我们枚举到i行,j表示这一行的状态,k表示上一行的状态,这时的炮兵的最大数量即是属性
转移方程:
我们处理这一行时,首先要判断枚举到的某个状态是否在山上,如果没有山地,那么枚举上一行的状态,如果不在山地,且不与本行冲突,那么枚举上两行的状态,如果不在山上,且不与本行冲突,不与上一行冲突,那么就可以进行状态转移。
$f[i][j][k]=max(f[i][j][k],f[i-1][k][p]+num[j])$
初始化细节提示:
初始化时,我们首先要存所有满足条件的状态的炮兵的数量,以及满足条件的状态,然后我们预处理第二行即可。
代码:
/*
__builtin_popcount = int
__builtin_popcountl = long int
__builtin_popcountll = long long
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod= 998244353;
int n,m,f[110][1ll<<10][1ll<<10],cnt,st[1ll<<10],num[1ll<<10],ans;
int a[110];
void init(){
for (int s = 0; s <(1ll<<m) ; ++s) {
if(!(s&(s<<1ll))&&!(s&(s<<2ll))){
st[++cnt]=s;
num[cnt]=__builtin_popcount(s);
}
}
}
void solve(){
cin>>n>>m;
for (int i = 1; i <=n ; ++i) {
for (int j = 1; j <=m ; ++j) {
char s;
cin>>s;
if(s=='H')a[i]+=1ll<<(j-1);
}
}
init();
for (int i = 1; i <=cnt ; ++i) {
for (int j = 1; j <=cnt ; ++j) {
if(!(st[i]&st[j])&&!(st[i]&a[2])&&!(st[j]&a[1])){
f[2][i][j]=num[i]+num[j];
}
}
}
///f[i][j][k]表示第i行成立时,上一行是j状态,上两行是k状态的方案数。
for (int i = 3; i <=n ; ++i) {
for (int j = 1; j <=cnt ; ++j) {
if(!(st[j]&a[i])){
for (int lfirst = 1; lfirst <=cnt ; ++lfirst) {
if(!(st[lfirst]&a[i-1])&&!(st[lfirst]&st[j])){
for (int lsecond = 1; lsecond <=cnt ; ++lsecond) {
if(!(st[lsecond]&a[i-2])&&!(st[lsecond]&st[lfirst])&&!(st[lsecond]&st[j])){
f[i][j][lfirst]=max(f[i][j][lfirst],f[i-1][lfirst][lsecond]+num[j]);
ans= max(ans,f[i][j][lfirst]);
}
}
}
}
}
}
}
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while (t--)solve();
}