zoukankan      html  css  js  c++  java
  • P5056 【模板】插头dp

    思路

    插头DP的模板
    插头DP括号序列的方法其实就是利用回路不能交叉匹配,必定两两配对的性质通过括号序列描述了连通块的匹配关系
    注意分类讨论、判断状态合法
    最边上一圈不能有插头延伸过去,要注意

    代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define int long long
    using namespace std;
    int val[2][100001],times[2][100001],now,fir[100001],nxt[100001],cnt[2],n,m,endx,endy,mat[20][20],ans=0,last,pos[40];
    const int MOD = 100001;
    void insert(int c,int num){
        int t=c%MOD,i;
        for(i=fir[t];i;i=nxt[i]){
            if(val[now][i]==c){
                times[now][i]+=num;
                return;
            }
        }
        ++cnt[now];
        val[now][cnt[now]]=c;
        times[now][cnt[now]]=num;
        nxt[cnt[now]]=fir[t];
        fir[t]=cnt[now];
    }
    int Getchar(void){
        char c=getchar();
        while(c!='*'&&c!='.')
            c=getchar();
        if(c=='*')
            return 1;
        else
            return 0;
    }
    int getval(int x,int pos){
        return (x>>((pos-1)*2))%4;
    }
    // void print(int x){
    //     for(int i=0;i<2*(m+1);i++){
    //         printf("%lld",(x>>i)&1);
    //     }
    //     printf("
    ");
    // }
    void dp(void){
        now=0;
        insert(0,1);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=cnt[now];j++)
                val[now][j]<<=2;
            for(int j=1;j<=m;j++){
                last=now;
                now^=1;
                cnt[now]=0;
                // printf("i=%lld j=%lld
    ",i,j);
                // for(int k=1;k<=cnt[last];k++)
                //     print(val[last][k]);
                memset(fir,0,sizeof(fir));
                memset(nxt,0,sizeof(nxt));
                for(int k=1;k<=cnt[last];k++){
                    int state=val[last][k],num=times[last][k],plugL=getval(state,j),plugU=getval(state,j+1);
                    //新建一个联通分量
                    if(!mat[i][j]){
                        if((!plugL)&&(!plugU)){
                            if((!mat[i][j+1])&&(!mat[i+1][j]))
                                insert(state+pos[(j-1)*2]+pos[j*2+1],num);
                        }
                        //合并两个联通分量
                        else if(plugL&&plugU){
                            if(plugL==1&&plugU==2){
                                if(i==endx&&j==endy){
                                    // printf("i=%lld j=%lld num=%lld
    ",i,j,num);
                                    // print(state);
                                    ans+=num;
                                }
                            }
                            if(plugL==2&&plugU==1){
                                insert(state-pos[(j-1)*2+1]-pos[j*2],num);
                            }
                            if(plugL==1&&plugU==1){
                                int k1=1;
                                for(int l=j+2;l<=m+1;l++){
                                    if(getval(state,l)==0)
                                        continue;
                                    if(getval(state,l)==1)
                                        k1++;
                                    if(getval(state,l)==2)
                                        k1--;
                                    if(!k1){
                                        insert(state-pos[(j-1)*2]-pos[j*2]-pos[(l-1)*2],num);
                                        break;
                                    }
                                }
                            }
                            if(plugL==2&&plugU==2){
                                int k2=1;
                                for(int l=j-1;l>=1;l--){
                                    if(getval(state,l)==0)
                                        continue;
                                    if(getval(state,l)==1)
                                        k2--;
                                    if(getval(state,l)==2)
                                        k2++;
                                    if(!k2){
                                        insert(state-pos[(j-1)*2+1]-pos[j*2+1]+pos[(l-1)*2],num);
                                        break; 
                                    }
                                }
                            }
                        }
                        //延续一个联通分量
                        else{
                            if(plugL&&(!plugU)){
                                //左->下
                                if(!mat[i+1][j])
                                    insert(state,num);
                                //左->左
                                if((!mat[i][j+1]))
                                    insert(state-pos[(j-1)*2]*plugL+pos[j*2]*plugL,num);
                            }
                            if(plugU&&(!plugL)){
                                //上->上
                                if(!mat[i+1][j]){
                                    insert(state-pos[j*2]*plugU+pos[(j-1)*2]*plugU,num);
                                }
                                //上->左
                                if(!mat[i][j+1])
                                    insert(state,num);
                            }
                        }
                    }
                    else{
                        if((!plugL)&&(!plugU)){
                            insert(state,num);
                        }
                    }
                }
            }
        }
    }
    signed main(){
        // freopen("test.out","w",stdout);
        scanf("%lld %lld",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                mat[i][j]=Getchar();
                if(mat[i][j]==0){
                    endx=i;
                    endy=j;
                }
            }
        for(int i=0;i<=m+1;i++)
            mat[0][i]=mat[n+1][i]=1;
        for(int i=1;i<=n;i++)
            mat[i][0]=mat[i][m+1]=1;
        
        // for(int i=1;i<=n;i++){
        //     for(int j=1;j<=m;j++)
        //         printf("%lld ",mat[i][j]);
        //     printf("
    ");
        // }
        pos[0]=1;
        for(int i=1;i<30;i++)
            pos[i]=pos[i-1]<<1;
        // for(int i=0;i<30;i++)
        //     printf("%lld
    ",pos[i]);
        dp();
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    “贴身外教”是英语口语的学习方法综合解决方案
    浅谈提高英语口语的最有效方法
    【PERL】Perl默认的内部变量
    Perl——哈希的创建和引用
    linux和windows下,C/C++开发的延时函数,sleep函数
    linux标准库#include <unistd.h>与windows的#include <windows.h>(C语言开发)
    linux 如何显示一个文件的某几行(中间几行)
    使程序在Linux下后台运行
    Perl中关于数组的输出——需要注意的地方
    linux下C/C++,多线程pthread
  • 原文地址:https://www.cnblogs.com/dreagonm/p/10836949.html
Copyright © 2011-2022 走看看