题面如图所示
题目大意:
现在有一个n*m的图,P点可以作为根据地,放置士兵以后以该点为中心有个十字架攻击范围为2.
H点不能够放置士兵但是可以被攻击。现在要求对于给定的n * m的图,求能安置最多的士兵数量。且相互不能攻击到。
题目分析:先把地图压缩为二进制形式,对于一行,枚举它的所有状态K【0,2m -1】,记录合法状态.
要求满足K>>1&K为0且K>>2&K为0,因为相邻2以内不能攻击。
记录该状态能摆放的士兵数。即二进制上有多少个1。
记dp[i][j][k] 表示第i行的状态为k,i-1行的状态为j时能摆放的最大士兵数
枚举所有合法状态S与第一行的压缩地图P尝试是否能够兼容,即S是P的子集。记为dp[1][i][1]= people[i] 表示该行放置第i个合法状态的士兵,且上一行不放置任何。
接下来从下一行开始枚举到第n行,枚举三层合法状态j,k,q,依次代表为i-2,i-1,i 的状态,然后判断该三个状态是否会上下攻击,该三个状态能否兼容到自己对应的行的压缩地图。
转移方程dp[i][q][k]=max(dp[i][k][q],dp[i-1][k][j]+people[q])
最终求答案即为max(dp[n][i][j]) 枚举所有状态
代码如下
const int N=(1<<10)+10;
char s[110][20];
ll group[110];
ll can[N];
ll cnt=0;
ll peo[N];
ll dp[105][N][N];
signed main()
{
ll n,m;
read(n);
read(m);
for(int i=1; i<=n; i++)
{
scanf("%s",s[i]);
for(int j=0; j<m; j++)
{
if(s[i][j]=='P')
{
group[i]=(group[i]<<1)+1;
}
else
{
group[i]=(group[i]<<1);
}
}
}
ll kk=(1<<m)-1;
for(int i=0; i<=kk; i++)
{
if(((i<<1)&i)==0&&((i<<2)&i)==0)
{
can[++cnt]=i;
for(int j=i; j; j=(j>>1))
{
if(j&1)
{
peo[cnt]++;
}
}
}
}
for(int i=1; i<=cnt; i++)
{
if((group[1]&can[i])==can[i])
dp[1][1][i]=peo[i];
}
for(int i=2; i<=n; i++)
{
for(int j=1; j<=cnt; j++) // i-2
{
for(int k=1; k<=cnt; k++) //i-1
{
for(int q=1; q<=cnt; q++) // i
{
if((group[i]&can[q])==can[q]&&(group[i-1]&can[k])==can[k])
{
if((can[q]&can[k])==0&&(can[q]&can[j])==0&&(can[j]&can[k])==0)
{ // printf("Q %lld %lld %lld\n",)
dp[i][k][q]=max(dp[i][k][q],dp[i-1][j][k]+peo[q]);
}
}
}
}
}
}
ll ma=0;
for(int i=1; i<=cnt; i++)
{
for(int j=1; j<=cnt; j++)
{
ma=max(ma,dp[n][i][j]);
}
}
printf("%lld",ma);
}