[bzoj1210][HNOI2004]邮递员【插头dp】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=1210
【题解】
  一道插头dp的入门题。
  粗略来说就是从上往下,从左往右一格一格dp,用状态压缩的方法记录与未搜索格子的连通性(最小表示法或括号法)。
  具体可见陈丹琦的:《基于连通性状态压缩的动态规划问题》
  时间复杂度O(NM22(M+1)) 实际远远不到。
  

/* --------------
    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;
}
posted @ 2018-04-16 21:55  Vanisher  阅读(92)  评论(0编辑  收藏  举报