题目大意
给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路。问有多少种铺法?n,m(2<=n,m<=12)
插头dp:是一种基于连通性状态压缩的动态规划问题,概念性知识就不讲了,直接来做吧!
状态:
此题面对一种状态,只考虑上方(我们叫它下插头)和左方(我们叫它右插头)插头情况
如果您暂时不懂插头的含义,慢慢来看下去
$b1$、$b2$分别表示插头状态,我们知道一条线,是有左端点和右端点的,0表示无插头,1表示左端点,2表示右端点
来看这副图,在$(3,4)$的轮廓线上此时有四个插头,用括号表示法状态:(##)(##),用四进制表示法状态:$10021002$
当前点不能走,只有一种状态
$(1)$则$!b1$ $!b2$才能产生状态转移,有插头就代表会走到该点,
不能走当然得上面没有插头,这里就不放图了,脑补$qwq$
当前点能走
$(1)$ $!b1$ $!b2$,则要加两个插头确保该点走过 只有以前的图了,以后再补上,将就看吧
$(2)$ $!b1$ $b2$ 则向下延伸,直走或往右拐
$(3)$ $b1$ $!b2$则向右延伸,直走或向下拐
$(4)$ $b1==1$ $b2==1$ 发现了吗?这里合并后并非直接删去插头即可
得$O(m)$往右扫找另一边匹配的右括号,让两个右括号匹配 只有以前的图了,以后再补上,将就看吧
$(5)$ $b1==2$ $b2==2$ 和上面一样$O(m)$往左扫找另一边的左括号,让两个左括号匹配,脑补吧$qwq$
$ (6)$ $b1==1$ $b2==1$ 直接删掉插头就好了,两边的插头会自动匹配
$(7)$ $b1==2$ $b2==1$ 如出现这种状态,则达到了最终闭合状态,如果当前点为终点,更新答案,否则状态不合法
本题利用$hash$和滚动节省空间,为方便与快捷用位运算实现四进制,具体细节看代码
My complete code:
#include<cstdio> #include<cstring> using namespace std; typedef long long LL; const LL maxn=13; const LL hs=299987; LL n,m,ex,ey,now,last,ans; LL a[maxn][maxn],head[300000],next[2<<24],que[2][2<<24],val[2][2<<24],cnt[2],inc[13]; inline void init(){ scanf("%lld%lld",&n,&m); for(LL i=1;i<=n;++i){ char s[100]; scanf(" %s",s+1); for(LL j=1;j<=m;++j){ if(s[j]=='.'){ a[i][j]=1; ex=i; ey=j; } } } inc[0]=1; for(LL i=1;i<=13;++i) inc[i]=inc[i-1]<<2; } inline void insert(LL bit,LL num){ LL u=bit%hs+1; for(LL i=head[u];i;i=next[i]){ if(que[now][i]==bit){ val[now][i]+=num; return; } } next[++cnt[now]]=head[u]; head[u]=cnt[now]; que[now][cnt[now]]=bit; val[now][cnt[now]]=num; } inline void work(){ cnt[now]=1; val[now][1]=1; que[now][1]=0; for(LL i=1;i<=n;++i){ for(LL j=1;j<=cnt[now];++j) que[now][j]<<=2; for(LL j=1;j<=m;++j){ memset(head,0,sizeof(head)); last=now; now^=1; cnt[now]=0; for(LL k=1;k<=cnt[last];++k){ LL bit=que[last][k],num=val[last][k]; LL b1=(bit>>((j-1)*2))%4,b2=(bit>>(j*2))%4; if(!a[i][j]){ if(!b1&&!b2) insert(bit,num); }else if(!b1&&!b2){ if(a[i+1][j]&&a[i][j+1]) insert(bit+inc[j-1]+inc[j]*2,num); }else if(!b1&&b2){ if(a[i][j+1]) insert(bit,num); if(a[i+1][j]) insert(bit-inc[j]*b2+inc[j-1]*b2,num); }else if(b1&&!b2){ if(a[i+1][j]) insert(bit,num); if(a[i][j+1]) insert(bit-inc[j-1]*b1+inc[j]*b1,num); }else if(b1==1&&b2==1){ LL k1=1; for(LL l=j+1;l<=m;++l){ if((bit>>(l*2))%4==1) ++k1; if((bit>>(l*2))%4==2) --k1; if(!k1){ insert(bit-inc[j]-inc[j-1]-inc[l],num); break; } } }else if(b1==2&&b2==2){ LL k1=1; for(LL l=j-2;l>=0;--l){ if((bit>>(l*2))%4==1) --k1; if((bit>>(l*2))%4==2) ++k1; if(!k1){ insert(bit-inc[j]*2-inc[j-1]*2+inc[l],num); break; } } }else if(b1==2&&b2==1){ insert(bit-inc[j-1]*2-inc[j],num); }else if(i==ex&&j==ey){ ans+=num; } } } } } int main(){ init(); work(); printf("%lld",ans); return 0; }