1. 程式人生 > >bzoj1814 Ural 1519 Formula 1(插頭DP)

bzoj1814 Ural 1519 Formula 1(插頭DP)

return hide none add 位運算 復雜度 連通性 can 性問題

對插頭DP的理解還不是很透徹。

先說一下膚淺的理解吧。

插頭DP使用範圍:指數級復雜度,且適用於解決網格圖連通性問題,如哈密頓回路等問題。插頭一般指每相鄰2個網格的接口。

題目難度:一般不可做。

使用三進制狀態,用0表示沒有插頭,1表示左括號插頭,2表示右括號插頭,而由於位運算常數特別小,可以采用四進制+手工hash的做法。處理好狀態與編號的對應關系後進行dp,復雜度就只與合法狀態數有關了,時間復雜度O(snm^2),其中s為合法狀態數(不超過42000),而第二維的m顯然也是不滿的,因此可以通過本題。

狀態轉移詳見代碼(因為沒時間寫了,先咕著)

技術分享圖片
#include<bits/stdc++.h>
using
namespace std; typedef long long ll; const int N=3e5+7,mod=299989; int n,m,D,ex,ey,tot[2],mp[20][20],pw[20],hd[N],nxt[N],a[2][N]; ll ans,f[2][N]; char str[20]; void add(int x,ll y) { int u=x%mod+1; for(int i=hd[u];i;i=nxt[i])if(a[D][i]==x){f[D][i]+=y;return;} nxt[++tot[D]]=hd[u],hd[u]=tot[D]; a[D][tot[D]]
=x,f[D][tot[D]]=y; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%s",str+1); for(int j=1;j<=m;j++) if(str[j]==.){mp[i][j]=1,ex=i,ey=j;} } pw[0]=1; for(int i=1;i<=max(n,m);i++)pw[i]=pw[i-1]*4; a[0][1
]=0,tot[0]=f[0][1]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=tot[D];j++)a[D][j]*=4; for(int j=1;j<=m;j++) { memset(hd,0,sizeof hd); tot[D^=1]=0; for(int k=1;k<=tot[D^1];k++) { int S=a[D^1][k],b1=(S>>(j*2-2))%4,b2=(S>>(j*2))%4; ll p=f[D^1][k]; if(!mp[i][j]) { if(!b1&&!b2)add(S,p); } else if(!b1&&!b2) { if(mp[i+1][j]&&mp[i][j+1])add(S+pw[j-1]+2*pw[j],p); } else if(!b1&&b2) { if(mp[i][j+1])add(S,p); if(mp[i+1][j])add(S-b2*pw[j]+b2*pw[j-1],p); } else if(b1&&!b2) { if(mp[i+1][j])add(S,p); if(mp[i][j+1])add(S-b1*pw[j-1]+b1*pw[j],p); } else if(b1==b2) { if(b1==1) { int tmp=1; for(int l=j+1;l<=m;l++) { if((S>>(l*2))%4==1)tmp++; if((S>>(l*2))%4==2)tmp--; if(!tmp){add(S-pw[l]-pw[j-1]-pw[j],p);break;} } } if(b1==2) { int tmp=1; for(int l=j-2;l>=0;l--) { if((S>>(l*2))%4==1)tmp--; if((S>>(l*2))%4==2)tmp++; if(!tmp){add(S+pw[l]-2*pw[j-1]-2*pw[j],p);break;} } } } else if(b1==2&&b2==1)add(S-2*pw[j-1]-pw[j],p); else if(i==ex&&j==ey)ans+=p; } } } printf("%lld",ans); }
View Code

bzoj1814 Ural 1519 Formula 1(插頭DP)