啊,就是插头dp啦。其实这道算是水一点的。。。
把障碍的行和列当成格子的边框。不过统计步数时注意跨过边框算一步。
维护一下轮廓线上有插头的位置。由于这道题随便两个都可以匹配所以根本不需要记插头的匹配。
f[i][j][k] 表示现在到点(i,j)轮廓线上状态为k的最小步数。然后逐格转移就好了。
#include <bits/stdc++.h>
using namespace std;
int dx[4]={0,-1,0,1};
int dy[4]={-1,0,1,0};
int n,m,inf,cnt;
char s1[31][110],s[110][31];
int f[2][26][(1<<13)+10],blo[110][31][4];
void upd(int &x,int y){x=min(x,y);}
int trs(int x,int y)
{
x=(x>>2)<<1;x|=y&1;
x|=((y&2)>>1)<<m;
return x;
}
int main()
{
//freopen("tt.in","r",stdin);
scanf("%d%d",&n,&m);gets(s1[0]+1);
for(int i=1;i<=n;i++)gets(s1[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
s[j][i]=s1[i][j],cnt+=(s1[i][j]=='X');
swap(n,m);
for(int i=2;i<n;i+=2)
for(int j=2;j<m;j+=2)
for(int k=0;k<4;k++)
blo[i>>1][j>>1][k]=(s[i+dy[k]][j+dx[k]]!=' ');
n/=2;m/=2;
memset(f[1],0x3f,sizeof(f[1]));
inf=f[1][1][0];f[1][1][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(j==m)
memset(f[~i&1],0x3f,sizeof(f[~i&1]));
for(int k=0;k<1<<m+1;k++)
{
if(f[i&1][j][k]==inf)continue;
int *nex;
if(j==m)nex=f[~i&1][1];
else nex=f[i&1][j+1];
int v1=blo[i][j][2],v2=blo[i][j][3],val=f[i&1][j][k];
if(s[i<<1][j<<1]=='X')
{
if((k&3)==3)continue;
if((k&3)==0)
{
if(!v2)upd(nex[trs(k,1)],val+1);
if(!v1)upd(nex[trs(k,2)],val+1);
}
else upd(nex[trs(k,0)],val);
}
else
{
if((k&3)==3)upd(nex[trs(k,0)],val+1);
else if((k&3)==0)
{
upd(nex[trs(k,0)],val);
if(!v1&&!v2)upd(nex[trs(k,3)],val+3);
}
else
{
if(!v2)upd(nex[trs(k,1)],val+2);
if(!v1)upd(nex[trs(k,2)],val+2);
}
}
}
}
printf("%d\n",f[(n+1)&1][1][0]+cnt/2);
return 0;
}