思路分析:
这种棋盘类型的问题+数据范围。看起来就巨像状态压缩。
和玉米田那道题特别想,只不过那道题只攻击相邻的,这里的话攻击范围加大了一个格子。
状态表示
我们以不同的行 为不同的状态进行划分。由于这里第i行的摆放状态会受到i-1行和i-2行的同时的影响。因此,之前的状态表示f[i][j]不够用了。现在需要开到三维。 ,不对,而应该理解成第i-2行的状态同时会影响到i-1行和i行。 枚举合法的第 i−2i−2 层状态进行转移
- 在玉米田中:
- 在炮兵阵地中:
状态转移
- 转移的合法性限制
相邻距离小于等于二的两列,不能互相攻击到。
- 自身的合法性限制
在同一行内,大炮不能互相攻击到。
如果位置i有大炮,那么i+1和i+2的位置必须为0,两个同时为0 为true的话需要用||操作。
大炮不能摆放在山上。
状态初始化:
f[0][0][0] = 1
目标状态:
需要枚举第n行,所有合法的状态j,k。然后取一个最大值
和前面一样,f[n+2][0][0]表示n+2行的状态为0,n+1行状态为0的摆放方案的最大值。此时证明n行已经摆好了。就是我们想要的答案。
好几张图片取自:https://www.acwing.com/solution/content/57859/
优化:![在这里插入图片描述](https://img-blog.csdnimg.cn/cd28e626080446dead22e50815da317d.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6LaF57qn56CB5Yqb5aWl,size_20,color_FFFFFF,t_70,g_se,x_16)
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N = 110,M = 1 << 10;
int f[2][M][M]; // 第一维使用了滚动数组
vector<int>state;
vector<int>pre[M];
int n,m;
int g[N]; // g[i]存第i行的地图
int cnt[M]; // cnt[i] 记录i中1的个数
bool check(int state)
{
for(int i = 0 ; i < m ; i ++)
if((state >> i & 1)&&((state >> i + 1 & 1)||(state >> i + 2 & 1)))return false;
return true;
}
int count(int state)
{
int res=0;
for(int i = 0 ; i < m ; i ++)res += state >> i & 1;
return res;
}
int main()
{
cin>>n>>m;
for(int i = 1 ; i <= n ; i ++)
for(int j = 0 ; j < m ; j ++)
{
char c;
cin>>c;
if(c=='H')g[i] += 1 << j;
}
for(int i = 0 ; i < 1 << m ; i ++)
if(check(i))
{
state.push_back(i);
cnt[i] = count(i);
}
//预处理合法状态能转移的状态
for (auto state_st : state)
for (auto pre_st : state)
// 如果对应位没有同时为1
if (!(state_st & pre_st))
pre[state_st].push_back(pre_st);
// f[i][j][k] :考虑前 i 层,且第 i 层状态是 j,第 i−1 层状态是 k 的方案
// 枚举每一层
for(int i = 1 ; i <= n + 2 ; i ++)
for (auto i_st : state)//枚举当前层
if (!(g[i] & i_st))//第i层合法
for (auto j_st : pre[i_st])//枚举上一层
if (!(g[i - 1] & j_st))//第i - 1层合法
for (auto u_st : pre[j_st])//枚举上二层
if (!(i_st & u_st))//判断当前行与前两行是否冲突
// 每次求一个最大值
f[i & 1][j_st][i_st] = max(f[i & 1][j_st][i_st] ,
f[i - 1 & 1][u_st][j_st] + cnt[i_st]);
cout<<f[n + 2 & 1][0][0]<<endl;
return 0;
}