pd頭插
用途
插頭dp主要是用來解決基於連通性狀態壓縮的動態規划問題,一般來說,就是解決一個網格圖中的回路方案數的問題,並且數據范圍較小,比如這道模板
方法
定義插頭:路徑是否經過格點的邊,如圖就是一個左插頭
不難發現,對於一個回路上的所有點,都有且只有兩個插頭,如圖
於是我會爆搜\(\Theta(6^{nm})\)
這顯然不現實,考慮dp轉移
轉移的對象
考慮轉移輪廓線,也就是說一條將我們已經考慮的半圖和未考慮的半圖分開的線,如圖中綠線,藍線表示已經考慮
通常來說,我們采取逐格遞推(少數情況下,我們會考慮逐行遞推)
狀態的表示
最小表示法
障礙為0,第一個連通塊內編號為1,第二個連通塊內編號為2……
(還有一種是將每個連通塊標記成最左邊的列編號)
括號表示法
輪廓線上從左到右4個插頭 a, b, c, d,如果 a, c連通,並且與 b 不連通,那么 b, d 一定不連通。(對所有棋盤問題都適用,詳見CDQ論文)
那么不難聯想到括號匹配,用0表示無括號,1,表示左括號,2表示右括號(實現中一般采用4進制,位運算真香)
一些trival的細節
表示編碼一般考慮用map或hash滾動dp,要不然內存會爆
大致方式
就是分類瘋累討論,一個格子只有上下,左右,上左,上右,下左,下右6種情況,於是討論就完了,貼上模板的代碼
#include<bits/stdc++.h>
using namespace std;
namespace DEBUG {
void debug_out() { cerr << '\n'; }
template <typename Head, typename... Tail>
void debug_out(Head H, Tail... T) { cerr << ' ' << H, debug_out(T...); }
#define debug(...) cerr << '[' << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
} using namespace DEBUG;
typedef long long ll;
typedef unsigned long long ull;
//#define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?EOF:*S++)
namespace get_out
{
char B[1<<15],*S=B,*T=B;
char op;
inline void read_(int &x)
{
x=0;
int fi(1);
op=getchar();
while((op<'0'||op>'9')&&op!='-') op=getchar();
if(op=='-') op=getchar(),fi=-1;
while(op>='0'&&op<='9') x=(x<<1)+(x<<3)+(op^48),op=getchar();
x*=fi;
return;
}
inline void read_(long long &x)
{
x=0;
int fi(1);
op=getchar();
while((op<'0'||op>'9')&&op!='-') op=getchar();
if(op=='-') op=getchar(),fi=-1;
while(op>='0'&&op<='9') x=(x<<1)+(x<<3)+(op^48),op=getchar();
x*=fi;
return;
}
inline void read_(double &x)
{
x=0.0;
float fi(1.0),sm(0.0);
op=getchar();
while((op<'0'||op>'9')&&op!='-') op=getchar();
if(op=='-') op=getchar(),fi=-1.0;
while(op>='0'&&op<='9') x=(x*10.0)+(op^48),op=getchar();
if(op=='.') op=getchar();
int rtim=0;
while(op>='0'&&op<='9') sm=(sm*10.0)+(op^48),++rtim,op=getchar();
while(rtim--) sm/=10.0;
x+=sm,x*=fi;
return;
}
inline void postive_write(int x)
{
if(x>9) postive_write(x/10);
putchar(x%10|'0');
}
inline void postive_write(long long x)
{
if(x>9) postive_write(x/10);
putchar(x%10|'0');
}
inline void write_(int x)
{
if(x<0) x=-x,putchar('-');
postive_write(x);
}
inline void write_(int x,char ch)
{
write_(x),putchar(ch);
}
inline void write_(long long x)
{
if(x<0) x=-x,putchar('-');
postive_write(x);
}
inline void write_(long long x,char ch)
{
write_(x),putchar(ch);
}
}
using get_out::read_;
using get_out::write_;
#define maxn 15
#define inf 0x7f7f7f7f
#define mod 299993
#define hashMaxNum 300000
int n,m,head[hashMaxNum];
int cnt[2],mp[maxn][maxn],edi,edj,now,la;
ll ans=0;
class hahaha
{
public:
int s[20];
hahaha()=default;
hahaha(int state)
{
s[0]=state&3;
for(int i=1;i<=m;++i) s[i]=(state>>(i<<1))&3;
}
inline int rar()
{
int state=0;
for(int i=1;i<=m;++i) state|=s[i]<<(i<<1);
return (state|s[0]);
}
};
inline hahaha unpack(int state) {return hahaha(state);}
inline int rar(hahaha &H){return H.rar();}
class hash_T
{
public:
int state[2],nex;
ll num[2];
hash_T()=default;
}s[hashMaxNum];
inline void insert(int state,ll num)
{
int tmp=state%mod;
for(int i=head[tmp];i;i=s[i].nex)
if(state==s[i].state[now])
{
s[i].num[now]+=num;
return;
}
s[++cnt[now]].state[now]=state,
s[cnt[now]].nex=head[tmp],
s[head[tmp]=cnt[now]].num[now]=num;
}
hahaha now_state,_k;
ll lnum;
inline void zip_insert()
{
insert(rar(_k),lnum),_k=now_state;
}
inline void solve()
{
register int i,j,k;
int west,north;
insert(0,1);
for(i=1;i<=n;++i) for(j=1;j<=m;++j)
{
la=now,now^=1;//renew state
cnt[now]=0,memset(head,0,sizeof(head));//init
for(k=1;k<=cnt[la];++k)
{
now_state=unpack(s[k].state[la]),_k=now_state;
lnum=s[k].num[la],west=now_state.s[0],north=now_state.s[j];
if(!mp[i][j]) {if(!west&&!north) insert(rar(_k),lnum);continue;}
if(!west&&!north)
{
if(mp[i][j+1]&&mp[i+1][j])
_k.s[0]=2,
_k.s[j]=1,//掛過
zip_insert();
continue;
}
if(!west&&north)
{
if(mp[i+1][j])
insert(rar(_k),lnum);
if(mp[i][j+1])
_k.s[0]=north,
_k.s[j]=0,
zip_insert();
continue;
}
if(west&&!north)
{
if(mp[i][j+1])
insert(rar(_k),lnum);
if(mp[i+1][j])
_k.s[0]=0,
_k.s[j]=west,
zip_insert();
continue;
}
if(west==1&&north==2)
{
_k.s[0]=_k.s[j]=0;
bool flag=0;
for(int pos=0;pos<=m;++pos)
if(_k.s[pos])
{
flag=1;
break;
}
if(!flag&&i==edi&&j==edj) ans+=lnum;
continue;
}
if(west==2&&north==1)
{
_k.s[0]=_k.s[j]=0;
zip_insert();
continue;
}
if(west==1&&north==1)
{
int nm=1,pos=j+1;
for(;pos<=m;++pos)
{
nm+=_k.s[pos]==1?1:(_k.s[pos]==2?-1:0);
if(!nm) break;
}
_k.s[pos]=1;
_k.s[0]=_k.s[j]=0;
zip_insert();
continue;
}
if(west==2&&north==2)
{
int nm=-1,pos=j-1;
for(;pos;--pos)
{
nm+=_k.s[pos]==1?1:(_k.s[pos]==2?-1:0);
if(!nm) break;
}
_k.s[pos]=2;
_k.s[0]=_k.s[j]=0;
zip_insert();
continue;
}
}
}
}
signed main()
{
register char ch[20];
read_(n),read_(m);
for(int i=1;i<=n;++i)
{
scanf("%s",ch+1);
for(int j=1;j<=m;++j)
((ch[j]=='.'&&(mp[i][j]=1,edi=i,edj=j))||(mp[i][j]=0));
}
solve();
cout<<ans<<'\n';
return 0;
}