插头dp学习笔记

由于插头dp很难懂于是又来记笔记了
插头DP可以用来解决一些连通性状压问题。具体流程是分格子处理,然后可以根据需要进行滚动,状压一下轮廓线状态,常用4进制(?)之类的。
拿luogu例题做例子:

给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路。问有多少种铺法?n,m(2<=n,m<=12)

对于此题只考虑格子上方的插头和格子左方的插头情况,然后特殊的对于当前轮廓线拐角(DP位置)有b1,b2表示右插头和下插头,然后在四进制中,对于一个线段有0表示没有插头,1表示有左端点(左括号),2表示有右端点(右括号),括号互相匹配。
首先如果当前格子不能走那么只能从没有b1和b2的位置转移过来:

if(!a[i][j]){
    if(!b1&&!b2){
        ins(bit,num);
    }
}

然后对于当前格子能走分多种情况讨论:

!b1&&!b2

对于这种情况我们要加入两个插头(一个左插头一个右插头)来使这个点被走过。

else if(!b1&&!b2){
    if(a[i+1][j]&&a[i][j+1])
        ins(bit+bin[j-1]+bin[j]*2,num);
}

在代码中就是加了第j条线的左括号和j+1的右括号。

!b1&&b2

只有一个插头,并且是向下的,于是可以选择把这个插头向下延伸或者向右:

else if(!b1&&b2){
    if(a[i][j+1])
        ins(bit,num);
    if(a[i+1][j])
        ins(bit-bin[j]*b2+bin[j-1]*b2,num);
}

如果能向右延伸就向右延伸,如果能向下延伸,先把第j+1条线的插头去掉,改成第j条线的向下的插头。

b1&&!b2

有横着戳过来的插头,可以继续向右延伸或者改成向下。

else if(b1&&!b2){
    if(a[i+1][j])
        ins(bit,num);
    if(a[i][j+1])
        ins(bit-bin[j-1]*b1+bin[j]*b1,num);
}

另外上面两种情况里面\(\times b1/b2\)是因为不改变它们作为左括号还是右括号的性质。

b1=1&&b2=1

两个左括号相遇合并了,我们把这两个左括号删掉,但为了保证合法,把右边原来的一个右括号改成左括号。

else if(b1==1&&b2==1){
    int k1=1;
    F(l,j+1,m){
        if(((bit>>(l<<1))&3)==1)
            k1++;
        if(((bit>>(l<<1))&3)==2)
            k1--;
        if(!k1){
            ins(bit-bin[j]-bin[j-1]-bin[l],num);
            break;
        }
    }
}

b1=2&&b2=2

和上面类似,找到左边的左括号改成右括号。

else if(b1==2&&b2==2){
    int k1=1;
    D(l,j-2,0){
        if(((bit>>(l<<1))&3)==1)
            k1--;
        if(((bit>>(l<<1))&3)==2)
            k1++;
        if(!k1){
            ins(bit-bin[j]*2-bin[j-1]*2+bin[l],num);
            break;
        }
    }
}

b1=2&&b2=1

两个左右括号可以直接删掉了,虽然是背靠背的但是不影响,因为一定还有剩下的左右括号配对。

else if(b1==2&&b2==1){
    ins(bit-bin[j-1]*2-bin[j],num);
}

b1=1&&b2=2

说明这可以当做终点了,如果这是终点那么累加到答案里面,否则弃掉。

else if(i==ex&&j==ey)ans+=num;

然后这几个状态大概就可以处理所有情况了,放个全码:

#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace EMT{
    typedef long long ll;typedef double db;
    #define pf printf
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    #define int long long
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
    inline void file(){freopen("in.in","r",stdin);freopen("my.out","w",stdout);}
    inline int max(int a,int b){return a>b?a:b;}inline int min(int a,int b){return a<b?a:b;}
    inline void pi(int x){pf("%lld ",x);}inline void pn(){pf("\n");}
    const int N=15,mod=300000;
    int a[N][N],n,m,ex,ey,bin[N],now,lst,head[mod],next[2<<24],cnt[2],val[2][2<<24],key[2][2<<24];
    inline void ins(int bit,int num){
        ll v=bit%mod;
        for(int i=head[v];i;i=next[i]){
            if(key[now][i]==bit){
                val[now][i]+=num;
                return;
            }
        }next[++cnt[now]]=head[v];
        head[v]=cnt[now];
        val[now][cnt[now]]=num;
        key[now][cnt[now]]=bit;
    }
    inline short main(){
    //	file();
        std::cout<<std::oct;
        n=read(),m=read();int ans=0;
        F(i,1,n){
            F(j,1,m){
                char ch=getchar();
                while(ch!='*'&&ch!='.')ch=getchar();
                a[i][j]=(ch=='*'?0:1);
                if(a[i][j])ex=i,ey=j;
            }
        }bin[0]=1;
        F(i,1,m)bin[i]=bin[i-1]<<2;
        cnt[now]=1,val[now][1]=1;
        F(i,1,n){
            F(j,1,cnt[now])key[now][j]<<=2;
            F(j,1,m){
                memset(head,0,sizeof(head));
                lst=now,now^=1;
                cnt[now]=0;
                F(k,1,cnt[lst]){
                    int bit=key[lst][k],num=val[lst][k];
                    int b1=(bit>>((j-1)<<1))&3,b2=(bit>>(j<<1))&3;
                    if(!a[i][j]){
                        if(!b1&&!b2){
                            ins(bit,num);
                        }
                    }else if(!b1&&!b2){
                        if(a[i+1][j]&&a[i][j+1])
                            ins(bit+bin[j-1]+bin[j]*2,num);
                    }else if(!b1&&b2){
                        if(a[i][j+1])
                            ins(bit,num);
                        if(a[i+1][j])
                            ins(bit-bin[j]*b2+bin[j-1]*b2,num);
                    }else if(b1&&!b2){
                        if(a[i+1][j])
                            ins(bit,num);
                        if(a[i][j+1])
                            ins(bit-bin[j-1]*b1+bin[j]*b1,num);
                    }else if(b1==1&&b2==1){
                        int k1=1;
                        F(l,j+1,m){
                            if(((bit>>(l<<1))&3)==1)
                                k1++;
                            if(((bit>>(l<<1))&3)==2)
                                k1--;
                            if(!k1){
                                ins(bit-bin[j]-bin[j-1]-bin[l],num);
                                break;
                            }
                        }
                    }else if(b1==2&&b2==2){
                        int k1=1;
                        D(l,j-2,0){
                            if(((bit>>(l<<1))&3)==1)
                                k1--;
                            if(((bit>>(l<<1))&3)==2)
                                k1++;
                            if(!k1){
                                ins(bit-bin[j]*2-bin[j-1]*2+bin[l],num);
                                break;
                            }
                        }
                    }else if(b1==2&&b2==1){
                        ins(bit-bin[j-1]*2-bin[j],num);
                    }else if(i==ex&&j==ey)ans+=num;
                }
            }
        }pi(ans);
        return 0;
    }
}
signed main(){return EMT::main();}

刷题去了

posted @ 2022-01-10 14:34  letitdown  阅读(152)  评论(0编辑  收藏  举报