题目大意
给出一个 n ∗ m ( n ≤ 100 , m ≤ 10 ) n*m(n≤100,m≤10) n∗m(n≤100,m≤10)的棋盘,一些格子不能放置棋子。求最多能在棋盘上放置多少个棋子,使得每一行每一列的任两个棋子间至少有两个空格。
题目解析
状压 D P DP DP
先求出每一行的可行状态,保存到数组 S S S,并保存每种状态 1 1 1的个数为 C C C,即棋子的个数。
因为棋子的影响范围是 2 2 2格,所以我们向上枚举两行的状态以及当前行的状态
设 F [ i ] [ j ] [ k ] F[i][j][k] F[i][j][k]表示第 i i i行的状态为 j j j、第 i − 1 i-1 i−1行状态为 k k k时前 i i i行最多能放多少个棋子
F [ i ] [ j ] [ k ] = M a x ( 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]+C_j) F[i][j][k]=Max(F[i][j][k],F[i−1][k][l]+Cj) ( C j (C_j (Cj表示状态为 j j j时 1 1 1的个数 ) ) )
因为求第 i i i行只与 i − 1 i-1 i−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;
}