这道题重点在于不能有连续两个相邻的空格,不光是横向的连续两个空格的空格,还有纵向的连续两个空格也不行(把握这个这题就OK了)。之前做了一些铺瓷砖的压缩DP题,都没有在纵向有神马限制。这也是这道题的难点所在。
刚开始想的状态转移方程错了(想了好半天才发现是错的。。。T..T),原因是不能解决我说的内个重点问题,于是把内个加入到DP的状态当中。
DP[N][S][Q]表示第N行状态为S,Q表示第n行和第n-1行均为空的格子的状态,这些Q状态所表示的格子要在下一行放砖时填补。
还是老套路,先对每一行搜索,搜索怎么放瓷砖,这样得到两个状态s(本行新的状态),p(上一行的状态),于是,dp[n][s][q]=min{dp[n-1][p'][q']+k},其中p'与p不能有重合,q'必须满足q'&p=q',再根据这些状态算出q。
最后结果是min{dp[n-1][s][0]},s要满足保证没有相邻空格,
要用滚动数组,要在每次大循环后对一组状态清零。
附代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define M 130
#define N 72
#define u (1<<m)
#define cl(a) memset(a,-1,sizeof(a))
#define INF 500
using namespace std;
int dp[3][M][M],a[N],b[10],m,n,res;
int check(int t)
{
int i;
for (i=1;i<m;i++)
if (!(t&b[i-1])&&!(t&b[i])) return 0;
return 1;
}
void trans(int l,int s,int p1,int p,int k)
{
int i,q,t;
t=p1+a[l-1]+p;
int e=l%2;
for (i=0;i<=p;i++)
if ((i&p)==i)
{
q=u-1-(t|(s+a[l]));
if (dp[1-e][p1][i]<0) continue;
if (dp[e][s][q]<0) dp[e][s][q]=dp[1-e][p1][i]+k;
else dp[e][s][q]=min(dp[e][s][q],dp[1-e][p1][i]+k);
if (l==n&&check(s+a[l])&&!q) res=min(res,dp[e][s][q]);
}
}
void Rundp(int l,int i,int k,int s,int p)
{
int p1;
if (i>=m)
{
for (p1=0;p1<u;p1++)
if (!(p1&p)&&!(p1&a[l-1]))
{
if (check(p1+p+a[l-1])) trans(l,s,p1,p,k);
}
return;
}
if (a[l]&b[i]) Rundp(l,i+1,k,s,p);
else
{
Rundp(l,i+1,k,s,p);
if (!(a[l-1]&b[i])) Rundp(l,i+1,k+1,s+b[i],p+b[i]);
if (i<m-1&&!(a[l]&b[i+1])) Rundp(l,i+2,k+1,s+b[i]*3,p);
}
}
void clear(int l)
{
int i,j;
for (i=0;i<u;i++)
for (j=0;j<u;j++) dp[1-l%2][i][j]=-1;
}
int main()
{
int i,j;
string ss;
cin>>n>>m;
cl(dp);
for (i=0;i<m;i++) b[i]=(1<<i);
for (i=1;i<=n;i++)
{
cin>>ss;
for (j=0;j<m;j++)
{
if (ss[j]=='*') a[i]+=(1<<j);
}
}
res=INF;
a[0]=u-1;
dp[0][0][0]=0;
for (i=1;i<=n;i++)
{
Rundp(i,0,0,0,0);
clear(i);
}
cout<<res<<endl;
//system("pause");
return 0;
}