題意:用1×2的骨牌來鋪帶障礙格的矩形網格,使得每個網格至多被一張骨牌覆蓋且無法再加入骨牌(即沒有相鄰的未覆蓋無障礙網格)。求需要的最少骨牌數。
分析:輪廓線dp。每個格子的狀態用一個4進制數來表示,0代表未覆蓋,1代表已覆蓋且由骨牌右端或下端覆蓋,2代表已覆蓋且由骨牌左端覆蓋,3代表已覆蓋且由骨牌上端覆蓋。轉移按如下分類進行:
1.障礙格:將當前格狀態改成1
若不是情形1
2.左格(存在)狀態爲2或上格狀態爲3:將當前格狀態改成1
若不是情形1且不是情形2
3.考慮當前格狀態是否可爲2或3
4.考慮當前格是否可以不放骨牌
代碼
#include<bits/stdc++.h>
using namespace std;
const int maxS=1<<20;
struct node
{
vector<int> sta,f;
int mp[maxS];
void clear()
{
for (int i=0;i<sta.size();i++)
{
int S=sta[i];
mp[S]=-1;
}
sta.clear();f.clear();
}
void updata(int S,int f0)
{
int k=mp[S];
if (k!=-1) f[k]=min(f[k],f0);
else
{
sta.push_back(S);
f.push_back(f0);
k=sta.size();
mp[S]=k-1;
}
}
}nd[2];
int n,m,one[20],two[20],three[20];
char ch[200][20];
void init()
{
for (int i=0;i<2;i++) memset(nd[i].mp,-1,sizeof(nd[i].mp));
for (int i=1;i<=10;i++) one[i]=1<<(2*i-2),two[i]=1<<(2*i-1),three[i]=one[i]^two[i];
}
int get(int S,int j)
{
return (S&three[j])>>(2*j-2);
}
int make(int S,int j,int p)
{
S|=three[j];
if (p==0) return S^three[j];
if (p==1) return S^two[j];
if (p==2) return S^one[j];
return S;
}
int main()
{
init();
int T;cin>>T;
while (T--)
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%s",ch[i]+1);
int S0=0,pre=0,now=1;
for (int i=1;i<=m;i++) S0^=one[i];
nd[now].clear();nd[now].updata(S0,0);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
swap(pre,now);nd[now].clear();
for (int k=0;k<nd[pre].sta.size();k++)
{
int S=nd[pre].sta[k],f=nd[pre].f[k];
if (ch[i][j]=='*')
{
int S1=make(S,j,1);
nd[now].updata(S1,f);
}
else
{
bool flag=0;
if (j>1&&get(S,j-1)==2)
{
flag=1;
int S1=make(S,j,1);
nd[now].updata(S1,f);
}
if (get(S,j)==3)
{
flag=1;
int S1=make(S,j,1);
nd[now].updata(S1,f);
}
if (flag) continue;
if (j<m&&ch[i][j+1]=='.'&&get(S,j+1)!=3)
{
int S1=make(S,j,2);
nd[now].updata(S1,f+1);
}
if (i<n&&ch[i+1][j]=='.')
{
int S1=make(S,j,3);
nd[now].updata(S1,f+1);
}
if ((j==1||get(S,j-1))&&get(S,j))
{
int S1=make(S,j,0);
nd[now].updata(S1,f);
}
}
}
}
int ans=n*m;
for (int k=0;k<nd[now].f.size();k++) ans=min(ans,nd[now].f[k]);
printf("%d\n",ans);
}
return 0;
}