题目链接
https://www.luogu.com.cn/problem/P2704
题目大意易懂我就不解释了,直接上分析
分析:我们看图可以知道,大炮所在位置只对上下左右相邻的两个位置有影响,所以我们考虑直接从上到下枚举,这样就不用考虑下面的情况,只需要考虑上左右相邻两个位置是否冲突,我们设dp数组为dp[pre][now][i],表示当前考虑到了第i行并且状态为now,上一行的状态为pre的最优炮兵数,那么状态转移方程就是
dp[pre][now][i] = max(dp[pre][now][i],dp[ppre][pre][i-1]+sum[now]),ppre表示上上行,sum[now]表示状态为now时1的个数
,我们知道当前这一行的状态只受上两行状态的影响,所以我们不妨先预处理第一行和第二行所有的合法情况,然后从第三行开始状压dp,去维护dp的所有可行状态的最大值即可,但是需要注意的是,这里n达到了100,所以dp[pre][now][i]这里的i我们不能设置为100,不然会MLE,我们需要采用滚动数组,根据前面的状态转移方程我们可以看到,当前第i行的更新只会涉及到第i-1行,所以我们只需要把dp数组的第三维大小开到2即可(滚动数组),这样状态转移方程就变为了dp[pre][now][i%2] = max(dp[pre][now][i%2],dp[ppre][pre][(i-1)%2]).
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int maze[105]={0};//每一行的地形状态
int sum[1<<10] = {0};
ll f[1<<10][1<<10][2]={0};/*f[L][S][i]表示,考虑到了第i行需要滚动数组优化空间,当前状态为s
上一个状态为L的最大放置炮兵的数量*/
int get_number(int number)
{
int ans = 0;
while(number)
{
ans++;
number&=(number-1);
}
return ans;
}
int n,m;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
char str[20];
cin>>str;
for(int j=1;j<=m;j++)
{
if(str[j-1]=='H')
maze[i]+=(1<<(m-j));//处理地形
}
}
int N = 1<<m;
for(int i=0;i<N;i++) sum[i] = get_number(i);//初始化sum数组
/*现予处理第一行多所有可行情况*/
for(int i=0;i<N;i++)
{
if(i&maze[0]) continue;//炮兵不能放在山上
if(i&(i<<1)) continue;
if(i&(i<<2)) continue;
f[0][i][0] = sum[i];
}
/*再根据第一行递推第二行的可行情况*/
for(int i=0;i<N;i++)//这一行
for(int j=0;j<N;j++)//上一行
{
/*不满足要求*/
if(i&j) continue;//与相邻两行冲突
if(i&i<<1) continue;//i与自己这一行冲突
if(i&i<<2) continue;//i与自己这一行冲突
if(j&j<<1) continue;//j与自己这一行冲突
if(j&j<<2) continue;//j与自己这一行冲突
if(i&maze[1]) continue;//山顶不能放炮兵
if(j&maze[0]) continue;//山顶不能放炮兵
f[j][i][1] = sum[i]+sum[j];
}
for(int i=2;i<n;i++)
{
for(int s=0;s<N;s++)//当前行
{
if((s&s<<1)||(s&s<<2)||(s&maze[i])) continue;
for(int L=0;L<N;L++)//上一行
{
if((s&L)||(L&maze[i-1])||(L&L<<1)||(L&L<<2)) continue;
for(int fl = 0;fl<N;fl++)//上上行
{
if((fl&s)||(fl&L)||(fl&maze[i-2])||(fl&fl<<1)||(fl&fl<<2)) continue;
f[L][s][i%2] = max(f[L][s][i%2],f[fl][L][(i-1)%2]+sum[s]);
}
}
}
}
ll ans = 0;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{
ans = max(ans,f[i][j][(n-1)%2]);
}
cout<<ans<<'\n';
return 0;
}