【Ural】1519. Formula 1 插头DP
【题目】1519. Formula 1
【题意】给定n*m个方格图,有一些障碍格,求非障碍格的哈密顿回路数量。n,m<=12。
【算法】插头DP
【题解】《基于连通性状态压缩的动态规划问题》 by CDQ(万恶之源T_T)
如果你想学最小表示法,当然首推kuangbinの博客。
基本思想是逐格推进,维护轮廓线的m+1个插头的状态,每个插头有一个编号,连通的插头编号相同。
由于只转移和记录有效状态,所以时空复杂度都大大优于普通的状压DP。
1.存储:容易发现连通编号至多0~6,所以用数字每三个二进制位存一个插头编号,用hash表存数字,同状态须合并来保证状态总数。
解码:强制从左到右是从高到低,倒着&7就能解码出数组了。
2.最小表示法:过程中会出现合并编号和新建编号的操作,通过暴力最小化的方法使得编号位0~6,称为最小表示法。
编码:最小化的过程体现在编码,用桶vis记老编号对应的新编号,扫一遍即可。新建的编号设为6。
3.转移:本题的障碍格可以直接不转移,因为插头也不会变化,下面讨论非障碍格的转移(依赖于左插头和上插头)。
Ⅰ存在左插头和上插头
如果同编号,那么只有最后一个非障碍格才能闭合,否则状态无效。
如果不同编号,可以连接,遍历所有插头将和左插头连通的编号改成上插头。
Ⅱ存在左插头或上插头
如果右能加插头就转移,如果下能加插头就转移。
Ⅲ不存在左插头和上插头
如果右和下都能加插头就转移,插头编号6。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int maxn=15,MOD=30007,S=1000010; int c[maxn],map[maxn][maxn],n,m,ex,ey; struct h{ int first[MOD],tot,nxt[S]; ll state[S],ans[S]; void init(){ memset(first,0,sizeof(first)); tot=0; } void insert(ll x,ll num){ for(int i=first[x%MOD];i;i=nxt[i]){ if(state[i]==x){ ans[i]+=num; return;//!!! } } state[++tot]=x;ans[tot]=num; nxt[tot]=first[x%MOD];first[x%MOD]=tot; } }f[2]; void decode(ll x){ for(int i=m;i>=0;i--){ c[i]=x&7; x>>=3; } } int vis[maxn];// ll encode(){ for(int i=1;i<=7;i++)vis[i]=0; int cnt=0;ll x=0; for(int i=0;i<=m;i++){ if(!c[i]){x<<=3;continue;} if(!vis[c[i]])vis[c[i]]=++cnt; x=(x<<3)|vis[c[i]]; } return x; } void solve(int cur,int x,int y){ for(int k=1;k<=f[cur^1].tot;k++){ decode(f[cur^1].state[k]); int left=c[y-1],up=c[y]; ll ans=f[cur^1].ans[k]; if(left&&up){ if(left==up){ if(x==ex&&y==ey){ c[y-1]=c[y]=0; f[cur].insert(encode(),ans); } } else{ c[y-1]=c[y]=0; for(int i=0;i<=m;i++)if(c[i]==left)c[i]=up; f[cur].insert(encode(),ans); } } else if(left||up){ int now=left|up; if(map[x+1][y]){ c[y-1]=now;c[y]=0; f[cur].insert(encode(),ans); } if(map[x][y+1]){ c[y-1]=0;c[y]=now; f[cur].insert(encode(),ans); } } else{ if(map[x+1][y]&&map[x][y+1]){ c[y-1]=c[y]=7; f[cur].insert(encode(),ans); } } } } char s[maxn]; int main(){ scanf("%d%d",&n,&m); memset(map,0,sizeof(map)); ex=ey=0; for(int i=1;i<=n;i++){ scanf("%s",s+1); for(int j=1;j<=m;j++)if(s[j]=='.'){ map[i][j]=1;// ex=i;ey=j; } else map[i][j]=0; } if(!ex){printf("0\n");return 0;} int cur=0; f[0].init();f[0].insert(0,1); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(map[i][j])cur^=1,f[cur].init(),solve(cur,i,j); } for(int j=1;j<=f[cur].tot;j++)f[cur].state[j]>>=3; } ll ans=0; for(int i=1;i<=f[cur].tot;i++)ans+=f[cur].ans[i]; printf("%lld\n",ans); return 0; }