插头 DP 学习笔记
这种方法一般用于解决网格图上的问题,前提是数据范围是可状压的
首先是概念
轮廓线:已处理和未处理格子的分界线
插头:一个格子通过某些方向和其他格子相连,这些位置叫做插头,就是相当于插线时的接口
接下来考虑如何设计状态,首先假设枚举到点 \((i,j)\)
因为要形成回路,首先要保证没有一个插头只有一端有线段,所以要记录存在情况
其次是可以发现上面插头一定是两两对应的,且对应的不能相连
所以要维护对应关系,所以可以把左边的记为 1,右边的记为 2
考虑当前格子那些能影响到它,就是左边和上边,记为 p1,p2
有一个结论,从左往右 4 个插头 a,b,c,d,若 a,c 联通,则 b,d 必然不联通,感性理解一下,如果联通,线必然交叉,无法实现
接下来是分类讨论,如果是障碍,那么一定要求 0,0 才能转移
p1=0,p2=0,此时必须新建插头,因为要有两个出边,只能向下向右
有一个不为 0,那么一端必须连接,另一端任意
p1=1,p2=1,把他们连起来,此时对应的右端点上,较靠左的插头变成左端
p1=2,p2=2,同理
p1=2,p2=1,此时两个插头必然处于不同的线上,直接连就可以了
p1=1,p2=2,此时由前面的结论,连上就形成回路,所以只能在最后一个格子连
当然,每次转移只关心上一个状态,所以位置的两维可以滚掉,状态数有限,所以可以哈希表处理
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#define ll long long
using namespace std;
const int N=3e5+5,mod=299989;
int n,m,p[15],tot[2],st[2][N],d,a[15][15];
ll f[2][N],ans;
struct hash_table{
int head[N],nxt[N];
void add(int x,ll s)
{
int id=x%mod+1;
for(int i=head[id];i;i=nxt[i])
{
if(st[d][i]==x)
{
f[d][i]+=s;
return ;
}
}
nxt[++tot[d]]=head[id];
head[id]=tot[d];
st[d][tot[d]]=x,f[d][tot[d]]=s;
}
void clear()
{
memset(head,0,sizeof(head));
tot[d]=0;
}
}T;
int main()
{
scanf("%d%d",&n,&m);
int edx=0,edy=0;
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
for(int j=1;j<=m;j++)
{
if(s[j-1]=='.') a[i][j]=1,edx=i,edy=j;
}
}
p[0]=1;
for(int i=1;i<=12;i++) p[i]=p[i-1]<<2;
tot[d]=f[d][1]=1,st[d][1]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=tot[d];j++) st[d][j]<<=2;
for(int j=1;j<=m;j++)
{
d^=1;
T.clear();
for(int k=1;k<=tot[d^1];k++)
{
int s=st[d^1][k];
int p1=(s>>(j*2-2))&3,p2=(s>>(j*2))&3;
ll v=f[d^1][k];
if(!a[i][j])
{
if(!p1&&!p2) T.add(s,v);
continue;
}
if(!p1&&!p2)
{
if(a[i][j+1]&&a[i+1][j]) T.add(s+2*p[j]+p[j-1],v);
}
else if(!p1&&p2)
{
if(a[i][j+1]) T.add(s,v);
if(a[i+1][j]) T.add(s-p2*p[j]+p[j-1]*p2,v);
}
else if(p1&&!p2)
{
if(a[i][j+1]) T.add(s-p1*p[j-1]+p[j]*p1,v);
if(a[i+1][j]) T.add(s,v);
}
else if(p1==1&&p2==1)
{
int cnt=1,l;
for(l=j+1;l<=m;l++)
{
if((s>>(l*2))%4==1) cnt++;
if((s>>(l*2))%4==2) cnt--;
if(!cnt) break;
}
T.add(s-p[j]-p[j-1]-p[l],v);
}
else if(p1==2&&p2==2)
{
int cnt=1,l;
for(l=j-2;l>=0;l--)
{
if((s>>(l*2))%4==1) cnt--;
if((s>>(l*2))%4==2) cnt++;
if(!cnt) break;
}
T.add(s-p[j]*2-p[j-1]*2+p[l],v);
}
else if(p1==2&&p2==1) T.add(s-2*p[j-1]-p[j],v);
else if(i==edx&&j==edy) ans+=v;
}
}
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号