假设我们当前已经填充成如图所示的情况:需要填写(i,j)这个格子了
那么:我们有两个大类,六种情况~~~
第一类:
(i,j)格子本身是个障碍点,不可达
第一类:
(i,j)格子本身是个障碍点,不可达
那么,应该是如图:
当左插头和上插头都不存在的时候,那么才是合法的状态,可以状态转移
第二类:
(i,j)可达
第一种情况:
左插头和上插头都存在,且ID号不同:说明需要合并连通分量(ID号相同合并也没有啥意义)
ID号相同且到达最后一个节点:
计算终态
第二种情况:
当只有左插头,没有上插头时候,我们可以发现,插头可以往右边走,也可以往下边走
同理,当只有上插头时,插头可以右走,也可以下走
这样,出现了四种状态转移
第三种情况:
当左右插头都没有时,因为这个点必须在回路上,所以要新生成一个连通分量,也是一种状态转移~~~
对着网上题解敲了两种~~~
第一种的注释配上图片应该还是蛮好理解的~(自我感觉良好)
#include <cstring> #include <cstdio> #include <algorithm> using namespace std; #define ll long long const int STATE = 1000000 + 10; const int HASH = 30007; const int MAXD = 15; int N,M; int ex,ey; int cur; int mp[MAXD][MAXD]; ll sum; struct HASHMAP{ int Size,Head[HASH],Next[STATE]; long long state[STATE]; long long f[STATE]; void init(){ memset(Head,-1,sizeof(Head)); Size=0; } void push(ll st,ll num){ ll h = st % HASH; for(int i=Head[h];i!=-1;i=Next[i]) if (state[i]==st){ f[i]+=num; return; } Next[Size]=Head[h]; Head[h]=Size; f[Size]=num; state[Size]=st; Size++; } }hm[2]; void decode(int *code,ll st){ for(int i=M;i>=0;i--){ code[i]=st&7; st>>=3; } } ll encode(int *code){ int ch[MAXD]; memset(ch,-1,sizeof(ch)); ch[0]=0; int cnt=1; ll st=0; for(int i=0;i<=M;i++){ if (ch[code[i]]==-1) ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code){ for(int i=M;i>=1;i--) code[i]=code[i-1]; code[0]=0; } //当前格子为障碍格子 void dpblock(int i,int j){ for(int k=0;k<hm[cur].Size;k++){ ll st=hm[cur].state[k]; int code[MAXD]; decode(code,st); int Left=code[j-1],Up=code[j]; //没有插头,才能生成新状态 //否则是非法状态 if (Left==0 && Up==0){ if (j==M) shift(code); hm[1-cur].push(encode(code),hm[cur].f[k]); } } } void dpblank(int i,int j){ for(int k=0;k<hm[cur].Size;k++){ ll st=hm[cur].state[k]; ll num=hm[cur].f[k]; int code[MAXD]; decode(code,st); int Left=code[j-1],Up=code[j]; if (Left>0 && Up>0){ if (Left != Up){ code[j-1] = code[j] = 0; for(int l=0;l<=M;l++) if (code[l]==Up) code[l]=Left; if (j==M) shift(code); hm[1-cur].push(encode(code),num); } //左插头和上插头相遇 //到达终点了,(ex,ey)标记的是最后一个非可达格子 else if (i==ex && j==ey){ sum += num; code[j-1] = code[j] = 0; if (j==M) shift(code); hm[1-cur].push(encode(code),num); } } //左插头和下插头存在某一个 //上面的情况已经判断了两个都存在的情况,所以这里不可能存在两个 else if (Left > 0 || Up > 0){ //(i,j)的右边可达,是个可达格子 if (mp[i][j+1]){ code[j-1]=0; //这里写的是加,因为有一个是0,所以表达的是延续这个插头状态 code[j]=Left + Up; if (j==M) shift(code); hm[1-cur].push(encode(code),num); } if (mp[i+1][j]){ code[j-1]=Left + Up; code[j]=0; if (j==M) shift(code); hm[1-cur].push(encode(code),num); } } else{ //(i,j)格子没有左插头和上插头 //(i,j)的下面和右边都是可达格子 //否则,(i,j)不在回路中 if (mp[i][j+1] && mp[i+1][j]){ int maxc=1; for(int l=0;l<=M;l++) maxc=max(maxc,code[l]); //新建一个连通分量 code[j-1]=code[j]=maxc+1; hm[1-cur].push(encode(code),num); } } } } void init(){ char a; ex=ey=0; memset(mp,0,sizeof(mp)); for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++){ scanf("%c",&a); if (a=='.'){ mp[i][j]=1; ex=i; ey=j; } } getchar(); } } void solve(){ sum=0; cur=0; hm[cur].init(); hm[cur].push(0,1); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++){ hm[1-cur].init(); if (mp[i][j]) dpblank(i,j); else dpblock(i,j); cur=1-cur; } //printf("%I64d\n",sum); printf("%lld\n",sum); } int main(){ //freopen("input.txt","r",stdin); while(scanf("%d%d",&N,&M)!=EOF){ getchar(); init(); if (ex==0){ puts("0"); continue; } solve(); } return 0; }
第二种写法来自我bin神的博客:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> using namespace std; const int MAXD = 15; const int HASH = 30007; const int STATE = 1000000 + 10; int N,M; int mp[MAXD][MAXD]; int code[MAXD]; int ch[MAXD]; int ex,ey; #define ll long long struct HASHMAP{ int Head[HASH],Next[HASH],Size; ll state[STATE]; ll f[STATE]; void init(){ Size = 0; memset(Head,-1,sizeof(Head)); } void push(ll st,ll ans){ int h=st%HASH; for(int i=Head[h];i!=-1;i=Next[i]) if (state[i] == st){ f[i] += ans; return; } state[Size]=st; f[Size]=ans; Next[Size]=Head[h]; Head[h]=Size++; } }hm[2]; void decode(int *code,int m,ll st){ for(int i=m;i>=0;i--){ code[i]=st&7; st>>=3; } } ll encode(int *code,int m){ int cnt=1; memset(ch,-1,sizeof(ch)); ch[0]=0; ll st=0; for(int i=0;i<=m;i++){ if (ch[code[i]]==-1) ch[code[i]]=cnt++; code[i]=ch[code[i]]; st<<=3; st|=code[i]; } return st; } void shift(int *code,int m){ for(int i=m;i>0;i--) code[i]=code[i-1]; code[0]=0; } void dpblank(int i,int j,int cur){ int Up,Left; for(int k=0;k<hm[cur].Size;k++){ decode(code,M,hm[cur].state[k]); Left=code[j-1]; Up=code[j]; if (Left && Up){ if (Left == Up){ if (i==ex && j==ey){ code[j-1] = code[j] = 0; if (j==M) shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else{ code[j-1] = code[j] = 0; for(int t=0; t<=M; t++) if (code[t] == Up) code[t] = Left; if (j==M) shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else if (Left + Up > 0){ if (mp[i][j+1]){ code[j-1]=0; code[j]=Left + Up; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } if (mp[i+1][j]){ code[j-1]=Left + Up; code[j]=0; if (j==M) shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } else{ if (mp[i][j+1] && mp[i+1][j]){ int maxc=1; for(int t=0;t<=M;t++) maxc=max(maxc,code[t]); code[j-1]=code[j]=maxc+1; hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } } } void dpblock(int i,int j,int cur){ for(int k=0;k<hm[cur].Size;k++){ decode(code,M,hm[cur].state[k]); if (code[j-1]==0 && code[j]==0){ if (j==M) shift(code,M); hm[cur^1].push(encode(code,M),hm[cur].f[k]); } } } char str[MAXD]; void init(){ memset(mp,0,sizeof(mp)); ex=0; for(int i=1;i<=N;i++){ scanf("%s",str); for(int j=0;j<M;j++) if (str[j]=='.'){ ex=i; ey=j+1; mp[i][j+1]=1; } } } void solve(){ int cur=0; ll ans=0; hm[cur].init(); hm[cur].push(0,1); for(int i=1;i<=N;i++) for(int j=1;j<=M;j++){ hm[cur^1].init(); if (mp[i][j]) dpblank(i,j,cur); else dpblock(i,j,cur); cur^=1; } for(int i=0;i<hm[cur].Size;i++) ans+=hm[cur].f[i]; printf("%lld\n",ans); } int main(){ //freopen("input.txt","r",stdin); while(scanf("%d%d",&N,&M)!=EOF){ init(); if (ex==0){ puts("0"); continue; } solve(); } return 0; }