题目大意
给出一个n∗m(n≤100,m≤10)n*m(n≤100,m≤10)n∗m(n≤100,m≤10)的棋盘,一些格子不能放置棋子。求最多能在棋盘上放置多少个棋子,使得每一行每一列的任两个棋子间至少有两个空格。
题目解析
状压DPDPDP
先求出每一行的可行状态,保存到数组SSS,并保存每种状态111的个数为CCC,即棋子的个数。
因为棋子的影响范围是222格,所以我们向上枚举两行的状态以及当前行的状态
设F[i][j][k]F[i][j][k]F[i][j][k]表示第iii行的状态为jjj、第i−1i-1i−1行状态为kkk时前iii行最多能放多少个棋子
F[i][j][k]=Max(F[i][j][k],F[i−1][k][l]+Cj)F[i][j][k]=Max(F[i][j][k],F[i-1][k][l]+C_j)F[i][j][k]=Max(F[i][j][k],F[i−1][k][l]+Cj) (Cj(C_j(Cj表示状态为jjj时111的个数)))
因为求第iii行只与i−1i-1i−1行有关,所以可以利用滚动数组优化
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,ans;
int a[105],b[1<<10],f[2][1<<11][1<<11],s[1<<10];
char c;
int bitnum(int x)
{
int sum;
for(sum=0;x;x-=x&-x,sum++);
return sum;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>c;
if(c=='P') a[i]|=(1<<m-j);
}
a[0]=(1<<m)-1;
for(int i=0;i<(1<<m);i++)
if((i&(i<<1))==0&&(i&(i<<2))==0&&(i&(i>>1))==0&&(i&(i>>2))==0)
b[++b[0]]=i,s[b[0]]=bitnum(i);
for(int i=1;i<=b[0];i++)
if((a[1]&b[i])==b[i])
f[0][b[i]][0]=s[i];
int x=0;
for(int i=2;i<=n;i++)
{
x^=1;
for(int j=1;j<=b[0];j++)
if((a[i]&b[j])==b[j])
for(int k=1;k<=b[0];k++)
if((a[i-1]&b[k])==b[k])
for(int l=1;l<=b[0];l++)
if((a[i-2]&b[l])==b[l])
if((b[j]&b[k])==0&&(b[j]&b[l])==0&&(b[k]&b[l])==0)
f[x][b[j]][b[k]]=max(f[x][b[j]][b[k]],f[x^1][b[k]][b[l]]+s[j]);
}
for(int i=1;i<=b[0];i++)
for(int j=1;j<=b[0];j++)
ans=max(ans,f[x][b[i]][b[j]]);
cout<<ans;
}