力求寫一篇全網最詳細的插頭dp
題目大意
給出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; }