状压DP
可以发现,选择一个格子能够覆盖周围4个格子和它自己,问题转化成在n*m的网格中,选出尽量少的格子把其他格子全部覆盖。又因为n*m<=40,有min(n,m)<=6,不妨设n<m
dp[i][st1][st2]表示考虑前i列,第i列被覆盖的状态为st1,第i+1列为st2,其中st是一个二进制数,表示一个集合,的第i位为1表示这一竖行的第i个格子已经被覆盖了,反之没有被覆盖。
那么dp[i-1][st1][st2]能转移dp[i][i_st1][i_st2]的条件是st2|i_st1=(1<<n)-1因为如果这时候第i-1列还不能被完全覆盖,以后就再也不能了。转移的时候枚举当前列选择出那些格子,并预处理CUR[st],表示当前列选出的格子集合为st时,当前列被覆盖的格子集合。而下一列被覆盖的集合就是st,不用预处理。
#include <bits/stdc++.h>
#define maxn 20
#define maxm 100009
#define MOD 1000000007
using namespace std;
int n,m;
int dp[50][1<<6][1<<6];
int CUR[1<<6],bitcount[1<<6];
int main()
{
scanf("%d%d",&n,&m);
if(n>m)
swap(n,m);
for(int st=0;st<1<<n;st++)
{
for(int j=0;j<n;j++)
{
if((1<<j)&st)
{
CUR[st]|=(1<<j);
if(j>0)
CUR[st]|=(1<<(j-1));
if(j<n-1)
CUR[st]|=(1<<(j+1));
bitcount[st]++;
}
}
}
for(int st=0;st<1<<n;st++)
{
dp[1][CUR[st]][st]=n-bitcount[st];
}
for(int i=2;i<=m;i++)
{
for(int st=0;st<1<<n;st++)
{
for(int last=0;last<1<<n;last++)
{
if((st|last)!=((1<<n)-1))
continue;
for(int cur=0;cur<1<<n;cur++)
{
dp[i][cur|CUR[st]][st]=max(dp[i][cur|CUR[st]][st],dp[i-1][last][cur]+n-bitcount[st]);
}
}
}
}
int ans=0;
for(int i=0;i<1<<n;i++)
ans=max(ans,dp[m][(1<<n)-1][i]);
printf("%d\n",ans);
//system("pause");
return 0;
}