P5056-[模板]插头dp

正题

题目链接:https://www.luogu.com.cn/problem/P5056


题目大意

\(n*m\)的网格,求有多少条回路可以铺满整个棋盘。


解题思路

插头\(dp\)的,写法是按照题解上的写法。

状态用的是括号匹配,然后用了哈希+邻接表(挂表)还有滚动数组优化空间

然后可以看题解学


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;
}
posted @ 2021-06-23 07:47  QuantAsk  阅读(58)  评论(0编辑  收藏  举报