bzoj1814 Ural 1519 Formula 1 插頭dp
阿新 • • 發佈:2018-12-23
Description
一個 n * m 的棋盤,有的格子存在障礙,求經過所有非障礙格子的哈密頓迴路個數
2 ≤ N, M ≤ 12
Solution
插頭dp入門題。。
我們設f[i,j,S]表示遞推到第i行第j列,輪廓線上插頭連通性狀態為S的方案數。所謂輪廓線就是已經dp過格子下邊界加上當前格子的右邊界組成的一條折線。所謂插頭就是路徑在輪廓線上的走向。
我們用三進製表示輪廓線狀態,0表示無插頭,1和2分別表示左右括號(一條路徑的左右端點)。轉移的時候分九種情況討論一下就可以了。
出於不知道的原因我的程式碼跑不過bzoj,跑過了timus
Code
#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
typedef long long LL;
const int N=41836;
int rc[14][14],bin[16],rec[N],rnk[1593229],n,m,tot;
LL f[13][13][N];
char str[13];
inline int get(int S,int p) {
return (S/bin[p])%3;
}
inline void dfs(int dep,int sum,int S) {
if (sum<0||sum+dep-1>m) return ;
if (dep>m) {
rec[++tot]=S,rnk[S]=tot;
return ;
}
dfs(dep+1,sum,S);
dfs(dep+1,sum+1,S+bin[dep]);
dfs(dep+1,sum-1,S+2*bin[dep]);
}
inline int left(int S,int i) {
int sum=0;
for (;~i;--i) {
int x=get(S,i);
if (x== 1) --sum;
else if (x==2) ++sum;
if (!sum) return bin[i];
}
return -1;
}
inline int righ(int S,int i) {
int sum=0;
for (;i<=m;++i) {
int x=get(S,i);
if (x==1) ++sum;
else if (x==2) --sum;
if (!sum) return bin[i];
}
return -1;
}
int main(void) {
freopen("data.in","r",stdin);
bin[0]=1; rep(i,1,15) bin[i]=bin[i-1]*3;
int tn,tm; LL ans=0; scanf("%d%d",&n,&m);
rep(i,1,n) {
scanf("%s",str+1);
rep(j,1,m) if (str[j]=='.') {
rc[i][j]=1; tn=i,tm=j;
}
}
dfs(0,0,0);
f[1][0][1]=1;
rep(i,1,n) {
rep(j,1,m) rep(k,1,tot) {
int S=rec[k],p=get(S,j-1),q=get(S,j);
LL tmp=f[i][j-1][k];
if (!rc[i][j]) {
if (!p&&!q) f[i][j][k]+=tmp;
} else {
if (!p&&!q&&rc[i+1][j]&&rc[i][j+1]) f[i][j][rnk[S+bin[j-1]+2*bin[j]]]+=tmp;
if (!p&&q) {
if (rc[i][j+1]) f[i][j][k]+=tmp;
if (rc[i+1][j]) f[i][j][rnk[S-q*(bin[j]-bin[j-1])]]+=tmp;
}
if (p&&!q) {
if (rc[i+1][j]) f[i][j][k]+=tmp;
if (rc[i][j+1]) f[i][j][rnk[S-p*(bin[j-1]-bin[j])]]+=tmp;
}
if (p==1&&q==1) f[i][j][rnk[S-bin[j-1]-bin[j]-righ(S,j)]]+=tmp;
else if (p==2&&q==2) f[i][j][rnk[S-2*(bin[j-1]+bin[j])+left(S,j-1)]]+=tmp;
else if (p==2&&q==1) f[i][j][rnk[S-2*bin[j-1]-bin[j]]]+=tmp;
else if (p==1&&q==2&&i==tn&&j==tm) ans+=tmp;
}
}
if (i!=n) rep(k,1,tot) if (rec[k]%3==0) f[i+1][0][k]+=f[i][m][rnk[rec[k]/3]];
}
printf("%lld\n", ans);
return 0;
}