[bzoj1210][HNOI2004]邮递员【插头dp】
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=1210
【题解】
一道插头dp的入门题。
粗略来说就是从上往下,从左往右一格一格dp,用状态压缩的方法记录与未搜索格子的连通性(最小表示法或括号法)。
具体可见陈丹琦的:《基于连通性状态压缩的动态规划问题》
时间复杂度 实际远远不到。
/* --------------
user Vanisher
problem bzoj-1210
----------------*/
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
using namespace std;
int read(){
int tmp=0, fh=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
return tmp*fh;
}
const int L=21, N=110,T=(1<<22);
struct INT{
int num[N],len;
void reget(){
int i;
for (i=1; i<=len||num[i]!=0; i++)
num[i+1]+=num[i]/10, num[i]=num[i]%10;
len=i-1;
}
void tonum(char *s){
len=strlen(s+1);
for (int i=1; i<=len; i++) num[len-i+1]=s[i]-'0';
}
void operator =(int tmp){
memset(this->num,0,sizeof(this->num));
this->len=0;
while (tmp!=0)
this->num[++(this->len)]=tmp%10, tmp/=10;
}
INT operator +(INT b){
INT c=*this; int i; c.len=max(c.len,b.len);
for (i=1; i<=c.len; i++) c.num[i]=c.num[i]+b.num[i];
c.reget(); return c;
}
INT operator *(INT b){
INT c; c.len=b.len+len-1; int i,j;
memset(c.num,0,sizeof(c.num));
for (i=1; i<=len; i++)
for (j=1; j<=b.len; j++)
c.num[i+j-1]=c.num[i+j-1]+num[i]*b.num[j];
c.reget(); return c;
}
INT operator *(int tmp){ INT b; b=tmp; return ((*this)*b);}
INT operator +(int tmp){ INT b; b=tmp; return ((*this)+b);}
void read(){ char s[N]; scanf("%s",s+1); tonum(s); }
void print(){
if (len==0){ printf("0\n"); return; }
for (int i=len; i>=1; i--) printf("%d",num[i]);
printf("\n");
}
}one,ans;
vector <INT> f[2];
vector <int> g[2];
int n,m,now[N],nex[N],f1,f2,lim,h[T],p[T],id;
int getnum(){
int num=0;
for (int i=0; i<m+1; i++)
num=num+(nex[i]<<(i*2));
return num;
}
void join(INT num){
int tmp=getnum();
if (h[tmp]==-1){
h[tmp]=f[f2].size();
p[f[f2].size()]=tmp;
f[f2].push_back(num);
g[f2].push_back(tmp);
}
else f[f2][h[tmp]]=f[f2][h[tmp]]+num;
}
int main(){
n=read(), m=read();
if (n==1||m==1){
printf("1\n");
return 0;
}
if (n<m) swap(n,m);
one=1;
f1=0, f2=1, lim=(1<<((m+1)*2));
f[f1].push_back(one);
g[f1].push_back(1+(2<<(1*2)));
memset(h,-1,sizeof(h));
for (int i=0; i<n; i++){
for (int j=0; j<m; j++){
if (i==0&&j==0) continue;
for (unsigned k=0; k<g[f1].size(); k++) h[p[k]]=-1;
for (unsigned k=0; k<g[f1].size(); k++){
if (g[f1][k]>=lim) continue;
INT num=f[f1][k];
for (int t=0; t<m+1; t++)
now[t]=nex[t]=(g[f1][k]>>(t*2))&3;
if (now[j]!=0&&now[j+1]!=0){
if (now[j]==1&&now[j+1]==2&&i==n-1&&j==m-1)
if (g[f1][k]-(now[j]<<(j*2))-(now[j+1]<<(j*2+2))==0)
ans=ans+num;
if (now[j]==2&&now[j+1]==1)
nex[j]=0, nex[j+1]=0, join(num);
if (now[j]==2&&now[j+1]==2){
nex[j]=0, nex[j+1]=0;
int t=j-1, cnt=0;
while (!(cnt==0&&now[t]==1)){
if (now[t]==1) cnt--;
if (now[t]==2) cnt++;
t--;
}
nex[t]=2; join(num);
}
if (now[j]==1&&now[j+1]==1){
nex[j]=0, nex[j+1]=0;
int t=j+2, cnt=0;
while (!(cnt==0&&now[t]==2)){
if (now[t]==2) cnt--;
if (now[t]==1) cnt++;
t++;
}
nex[t]=1; join(num);
}
}
if (now[j]==0&&now[j+1]!=0){
nex[j]=0, nex[j+1]=now[j+1]; join(num);
nex[j]=now[j+1], nex[j+1]=0; join(num);
}
if (now[j]!=0&&now[j+1]==0){
nex[j]=0, nex[j+1]=now[j]; join(num);
nex[j]=now[j], nex[j+1]=0; join(num);
}
if (now[j]==0&&now[j+1]==0){
nex[j]=1, nex[j+1]=2; join(num);
}
}
swap(f1,f2);
g[f2].clear(); f[f2].clear();
}
for (unsigned k=0; k<g[f1].size(); k++) g[f1][k]=g[f1][k]<<2;
}
ans=ans*2;
ans.print();
return 0;
}