轮廓线维护(m+1)个插头状态
4进制(更方便)维护括号表示法:${\#,(,)}={0,1,2}$
在代码中有注释
#include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; #define W 200005 int n,m,tx,ty,pk,nk,B[25]; int cnt[2],hd[W],nxt[W],s[2][W]; ll f[2][W],ans; char q[15][15]; void ins(int S,ll v){//hash表存状态 int p=S%W; for(int i=hd[p];i;i=nxt[i]) if(s[nk][i]==S){f[nk][i]+=v; return;} nxt[++cnt[nk]]=hd[p]; hd[p]=cnt[nk]; s[nk][cnt[nk]]=S; f[nk][cnt[nk]]=v; } inline int id(int x){return 1<<B[x];} void work(){ f[nk][1]=cnt[nk]=1; for(int i=1,j,k,u,t;i<=n;++i){ for(j=1;j<=cnt[nk];++j) s[nk][j]<<=2;//移去旧插头,加入新插头 for(j=1;j<=m;++j){ memset(hd,0,sizeof(hd)); pk=nk; nk^=1; cnt[nk]=0;//滚动数组优化 for(k=1;k<=cnt[pk];++k){ int S=s[pk][k]; ll v=f[pk][k]; int R=(S>>B[j-1])%4,D=(S>>B[j])%4;//R:右插头,D:上插头 if(q[i][j]=='*'){if(!R&&!D)ins(S,v); continue;} if(!R&&!D&&j<m) ins(S+id(j-1)+id(j)*2,v); if(R&&!D){if(j<m)ins(S-R*id(j-1)+R*id(j),v); ins(S,v);} if(!R&&D){if(j<m)ins(S,v); ins(S+D*id(j-1)-D*id(j),v);} if(R==1&&D==1) for(u=j+1,t=1;u<=m;++u){ if((S>>B[u])%4==1) ++t; if((S>>B[u])%4==2) --t; if(!t){ins(S-id(j-1)-id(j)-id(u),v); break;} } if(R==2&&D==2) for(u=j-2,t=1;u>=0;--u){ if((S>>B[u])%4==1) --t; if((S>>B[u])%4==2) ++t; if(!t){ins(S-id(j-1)*2-id(j)*2+id(u),v); break;} } if(R==2&&D==1) ins(S-id(j-1)*2-id(j),v); if(R==1&&D==2&&S==id(j-1)+id(j)*2&&i==tx&&j==ty) ans=v;//答案在最后一个非障碍格 }//各种分类讨论 } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=20;++i) B[i]=i<<1; for(int i=1;i<=n;++i){ scanf("%s",q[i]+1); for(int j=1;j<=m;++j) if(q[i][j]=='.') tx=i,ty=j; }work(); printf("%lld\n",ans); return 0; }