bzoj 1814 Ural 1519 Formula 1 ——插头DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1814
普通的插头 DP 。但是调了很久。注意如果合并两个 1 的话,不是 “把向右第一个 2 该成 1 ”,而是 “把向右第一个没有与 1 匹配的 2 改成 1 ”。
原来获取哈希值是用字符串哈希的方法,遍历12个位置;太慢。直接对某数取模作为哈希值,手写哈希表保证不会找错状态。大概 1e5 个状态?
在转移的时候看一下下方和右边有没有障碍、只做合法的转移的话,取答案的时候就不用在判断 “当前两个位置是 1 和 2 ”之外再判断 “其余位置都是 0 ” 了。
自己写的是 “如果当前位置是结束位置且当前两个插口是 1 和 2 就输出答案并 break ” 。这样无解的时候要手动输出 0 。
滚动数组。并且只遍历有效的状态。方法是用手写队列存 “得到转移” 的状态,用 vis 判断该状态是否已经在队列里;把下一层的队列做好之后,遍历下一层(不是这一层)的队列把 vis 清空即可。
注意 long long 。
哈希表的映射方式是:原状态 -> 哈希值 -> ++tot 地分配空间 -> 原状态 。“ -> ” 就是要记下的对应关系。因为哈希值要用数组记它对应哪个位置,所以哈希值不宜太大。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=15,M=1e5+5;//mention int n,m,bin[N],q[2][M],t[2]; ll dp[2][M]; bool b[N][N],vis[M],en[N][N]; namespace H{ const int md=1e6+3;// int hd[md+5],xnt,nxt[M],vl[M]; int Ps(int s) { int h=s%md; for(int i=hd[h];i;i=nxt[i]) if(vl[i]==s){return i;} vl[++xnt]=s; nxt[xnt]=hd[h]; hd[h]=xnt; return xnt; } } int get(int cr,int j){ return (cr&(bin[j+1]-bin[j]))>>(2*j);} void cz(int d,bool v){ if(!vis[d]) vis[d]=1,q[v][++t[v]]=d;} void solve() { bin[0]=1; int lm=m+1; for(int i=1;i<=lm;i++)bin[i]=bin[i-1]<<2; bool flag=0; for(int i=n;i&&!flag;i--) for(int j=m;j;j--) if(!b[i][j]){en[i][j]=flag=1;break;} bool u=0,v=1; flag=0; dp[u][H::Ps(0)]=1; q[u][++t[u]]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { for(int c=1;c<=t[u];c++) { int cr=H::vl[q[u][c]];ll tp=dp[u][q[u][c]];//ll!!! int d0=get(cr,j-1), d1=get(cr,j), t0,t1; if(b[i][j]) { if(!d0&&!d1) { t0=H::Ps(cr); cz(t0,v); dp[v][t0]+=tp;} continue; } if(en[i][j]) { if(d0==1&&d1==2) { printf("%lld\n",tp);flag=1;break;} continue; } if((!d0&&d1)||(!d1&&d0)) { t0=cr; t1=cr+d1*bin[j-1]-d0*bin[j-1]+d0*bin[j]-d1*bin[j]; if(i<n&&!b[i+1][j]) {if(d0){t0=H::Ps(t0); cz(t0,v); dp[v][t0]+=tp;} else{t1=H::Ps(t1); cz(t1,v); dp[v][t1]+=tp;}} if(j<m&&!b[i][j+1]) {if(d0){t1=H::Ps(t1); cz(t1,v); dp[v][t1]+=tp;} else{t0=H::Ps(t0); cz(t0,v); dp[v][t0]+=tp;}} continue; } if(!d0&&!d1&&i<n&&j<m&&!b[i+1][j]&&!b[i][j+1]) { t0=cr+bin[j-1]+2*bin[j]; t0=H::Ps(t0); cz(t0,v); dp[v][t0]+=tp; continue; } if(d0==1&&d1==1) { t0=cr-bin[j-1]-bin[j]; for(int p=j+1,top=0,d;p<=m;p++) { d=get(t0,p); if(d==1)top++; else if(d==2&&top)top--; else if(d==2){t0-=bin[p];break;} } t0=H::Ps(t0); cz(t0,v); dp[v][t0]+=tp; continue; } if(d0==2&&d1==2) { t0=cr-2*bin[j-1]-2*bin[j];//2*!!! for(int p=j-2,top=0,d;p>=0;p--) { d=get(t0,p); if(d==2)top++; else if(d==1&&top)top--; else if(d==1){t0+=bin[p];break;} } t0=H::Ps(t0); cz(t0,v); dp[v][t0]+=tp; continue; } if(d0==2&&d1==1) { t0=cr-2*bin[j-1]-bin[j]; t0=H::Ps(t0); cz(t0,v); dp[v][t0]+=tp; continue; } } if(flag)break; for(int c=1;c<=t[u];c++)dp[u][q[u][c]]=0; for(int c=1;c<=t[v];c++)vis[q[v][c]]=0;//v not u!!! t[u]=0; swap(u,v); } if(flag)break; for(int c=1;c<=t[u];c++) { int cr=H::vl[q[u][c]];ll tp=dp[u][q[u][c]];//ll!!! if(get(cr,m))continue; int d=cr<<2; d=H::Ps(d); cz(d,v); dp[v][d]=tp; } for(int c=1;c<=t[u];c++)dp[u][q[u][c]]=0; for(int c=1;c<=t[v];c++)vis[q[v][c]]=0; t[u]=0; swap(u,v); } if(!flag)puts("0");/// } int main() { scanf("%d%d",&n,&m); char ch[N]; for(int i=1;i<=n;i++) { scanf("%s",ch+1); for(int j=1;j<=m;j++) b[i][j]=(ch[j]=='*'); } solve(); return 0; }