正题
题目链接:https://www.luogu.com.cn/problem/P5056
题目大意
n∗mn*mn∗m的网格,求有多少条回路可以铺满整个棋盘。
解题思路
插头dpdpdp的,写法是按照题解上的写法。
状态用的是括号匹配,然后用了哈希+邻接表(挂表)还有滚动数组优化空间
然后可以看题解学
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int P=133331;
struct node{int to,next;
}a[P*2];
int n,m,o,tot,zx,zy,t[2],bit[25],ls[P],S[2][P],v[25][25];
long long ans,dp[2][P];
char st[25];
void Add(int s,long long v){int x=s%P;for(int i=ls[x];i;i=a[i].next)if(S[o][a[i].to]==s){dp[o][a[i].to]+=v;return;}t[o]++;dp[o][t[o]]=v;S[o][t[o]]=s;a[++tot].to=t[o];a[tot].next=ls[x];ls[x]=tot;return;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%s",st+1);for(int j=1;j<=m;j++)if(st[j]=='.'){v[i][j]=1;zx=i;zy=j;}}for(int i=0;i<=12;i++)bit[i]=(1<<(i<<1));t[o]=1;S[o][1]=0;dp[o][1]=1;for(int i=1;i<=n;i++){for(int j=1;j<=t[o];j++)S[o][j]<<=2;//将右插头移到最左边 for(int j=1;j<=m;j++){o^=1;tot=t[o]=0;memset(ls,0,sizeof(ls));int s,dpl,rpl;long long w;for(int k=1;k<=t[!o];k++){s=S[!o][k];w=dp[!o][k];dpl=(s>>(j<<1))%4;rpl=(s>>(j-1<<1))%4;if(!v[i][j]){//障碍 if(!dpl && !rpl)Add(s,w);//不能有插头 }else if(!dpl && !rpl){//两边都没有插头if(v[i+1][j]&&v[i][j+1])//开一个左上角插头 Add(s+2*bit[j]+bit[j-1],w);} else if(!dpl && rpl){//只有右插头 if(v[i+1][j])Add(s,w);if(v[i][j+1])Add(s-rpl*bit[j-1]+rpl*bit[j],w);}else if(dpl && !rpl){//只有下插头if(v[i+1][j])Add(s-dpl*bit[j]+dpl*bit[j-1],w);if(v[i][j+1])Add(s,w); }else if(dpl==1&&rpl==1){int c=1;for(int p=j+1;p<=m;p++){if((s>>(p<<1))%4==1)c++;if((s>>(p<<1))%4==2)c--;if(!c){Add(s-bit[j]-bit[j-1]-bit[p],w);break;}}}else if(dpl==2&&rpl==2){int c=1;for(int p=j-2;p>=0;p--){if((s>>(p<<1))%4==2)c++;if((s>>(p<<1))%4==1)c--;if(!c){Add(s-2*bit[j]-2*bit[j-1]+bit[p],w);break;}}}else if(dpl==1&&rpl==2)Add(s-2*bit[j-1]-bit[j],w);else if(dpl==2&&rpl==1)if(i==zx&&j==zy)ans+=w;}}}printf("%lld\n",ans);return 0;
}