貌似高中的时候就见过的这题,思路全在代码注释里了
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 120
using namespace std;
int bmp[maxn]; //用二进制表示后,压缩存储地图的每一行
int n,m;
int statu[maxn],sc; //保存一行内长度为 m 的满足互不攻击的排兵状态 ,预先处理,好思想
int dp[maxn][maxn][maxn]; // //dp[i][j][k] 表示第 i 行状态为 j ,第 i-1 行状态为 k 时的最大排兵数量
int num[maxn]; //num[i] 表示status[i] 而二进制里面 1 的数量
void init()
{
char str[maxn];
int c,res,u,v;
cin>>n>>m;
//读入地图并压缩
for(int i=1;i<=n;i++)
{
cin>>str;
res=0;
for(int j=0;j<m;j++)
{
if(str[j]=='P')
{
res=res|(1<<(m-1-j));
}
}
bmp[i]=res;
}
//预先求出满足条件的状态
sc=0;u=1<<m;
for(int i=0;i<u;i++)
{
if((i&(i<<1))==0&&(i&(i<<2))==0)
{
statu[sc]=i;
c=0;
v=i;
while(v)
{
c+=v%2;
v/=2;
}
num[sc++]=c;
}
}
}
void op2(int u) //输出 u 的二进制表示 ,调试用函数
{
if(u==0)return;
op2(u/2);
printf("%d",u%2);
}
void check() //检查状态压缩正确与否,调试用
{
for(int i=1;i<=n;i++)
{
printf("%2d ",bmp[i]);
op2(bmp[i]);
cout<<endl;
}
cout<<"-------------\n";
for(int i=0;i<sc;i++)
{
op2(statu[i]);
printf(" %d\n",num[i]);
}
}
void solve()
{
// check(); 此行代码调试用
memset(dp,0,sizeof(dp)); //dp[i][j][k] 表示第 i 行状态为 j ,第 i-1 行状态为 k 时的最大排兵数量
for(int i=0;i<sc;i++) //初始化第一行
{
if((statu[i]&bmp[1])!=statu[i])continue;
for(int j=0;j<sc;j++)
{
dp[1][i][j]=num[i];
}
}
for(int i=2;i<=n;i++)
{
for(int j=0;j<sc;j++) //枚举第 i 行的状态
{
if((bmp[i]&statu[j])!=statu[j])continue; //判断状态 j 能够放在第 i 行的地形上
for(int k=0;k<sc;k++) //枚举第 i-1 行的状态
{
if(statu[j]&statu[k])continue; //判断第 i 行和第 i-1 行是否冲突
for(int h=0;h<sc;h++) //枚举第 i-2 行状态
{
if((statu[j]&statu[h])||(statu[k]&statu[h]))continue; //判断第 i 行和第 i-2 行是否冲突,第 i-1行和第 i-2 行是否冲突,貌似这里不需要判断,因为如果i-1 行和 i-2行冲突的话,答案就是0 了,无所谓的.
dp[i][j][k]=max(dp[i-1][k][h],dp[i][j][k]);
}
dp[i][j][k]+=num[j];
}
}
}
int ans=0;
//欢快的找答案
for(int i=0;i<sc;i++)
{
for(int j=0;j<sc;j++)
{
if(statu[i]&statu[j])continue;
ans=max(ans,dp[n][i][j]);
}
}
printf("%d\n",ans); //你懂的
}
int main()
{
freopen("1185.txt","r",stdin);
init();
solve();
return 0;
}