zoukankan      html  css  js  c++  java
  • 【BZOJ】2331: [SCOI2011]地板 插头DP

    【题意】给定n*m的地板,有一些障碍格,要求用L型的方块不重不漏填满的方案数。L型方块是从一个方格向任意两个相邻方向延伸的方块,不能不延伸。n*m<=100。

    【算法】插头DP

    【题解】状态0表示无插头,1表示能拐弯的插头,2表示不能拐弯的插头。(有插头,方块就必须必须延伸到该格),考虑转移即可。

    注意可以凭空产生一个能拐弯的插头。

    n*m<=100,当m>10的时候将i,j互换。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int maxn=110,MM=20110520,MOD=50007,S=5000010;
    int map[maxn][maxn],n,m,c[maxn];
    int M(int x){return x>=MM?x-MM:x;}
    struct h{
        int state[S],ans[S],first[MOD],tot,nxt[S];
        void init(){
            memset(first,0,sizeof(first));
            tot=0;
        }
        void insert(int x,int num){
            for(int i=first[x%MOD];i;i=nxt[i])if(state[i]==x){
                ans[i]=M(ans[i]+num);
                return;
            }
            state[++tot]=x;ans[tot]=num;
            nxt[tot]=first[x%MOD];first[x%MOD]=tot;
        }
    }f[2];
    void decode(int x){for(int i=m;i>=0;i--)c[i]=x&3,x>>=2;}
    int encode(){int x=0;for(int i=0;i<=m;i++)x=(x<<2)|c[i];return x;}
    void solve(int cur,int x,int y){
        for(int k=1;k<=f[cur^1].tot;k++){
            decode(f[cur^1].state[k]);
            int ans=f[cur^1].ans[k],left=c[y-1],up=c[y];
            if(left==0){
                if(up==0){
                    if(map[x][y+1]&&map[x+1][y]){
                        c[y-1]=2;c[y]=2;
                        f[cur].insert(encode(),ans);
                    }
                    if(map[x+1][y]){
                        c[y-1]=1;c[y]=0;
                        f[cur].insert(encode(),ans);
                    }
                    if(map[x][y+1]){
                        c[y-1]=0;c[y]=1;
                        f[cur].insert(encode(),ans);
                    }
                }
                if(up==1){
                    if(map[x+1][y]){
                        c[y-1]=1;c[y]=0;
                        f[cur].insert(encode(),ans);
                    }
                    if(map[x][y+1]){
                        c[y-1]=0;c[y]=2;
                        f[cur].insert(encode(),ans);
                    }
                }
                if(up==2){
                    if(map[x+1][y]){
                        c[y-1]=2;c[y]=0;
                        f[cur].insert(encode(),ans);
                    }
                    c[y-1]=0;c[y]=0;
                    f[cur].insert(encode(),ans);
                }
            }
            if(left==1){
                if(up==0){
                    if(map[x+1][y]){
                        c[y-1]=2;c[y]=0;
                        f[cur].insert(encode(),ans);
                    }
                    if(map[x][y+1]){
                        c[y-1]=0;c[y]=1;
                        f[cur].insert(encode(),ans);
                    }
                }
                if(up==1){
                    c[y-1]=c[y]=0;
                    f[cur].insert(encode(),ans);
                }
            }
            if(left==2){
                if(up==0){
                    if(map[x][y+1]){
                        c[y-1]=0;c[y]=2;
                        f[cur].insert(encode(),ans);
                    }
                    c[y-1]=0;c[y]=0;
                    f[cur].insert(encode(),ans);
                }
            }
        }
    }
    char s[maxn];
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)if(s[j]=='_'){
                if(m<=10)map[i][j]=1;else map[j][i]=1;
            }
        }
        if(m>10)swap(n,m);
        int cur=0;f[0].init();f[0].insert(0,1);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++)if(map[i][j]){
                f[cur^=1].init(),solve(cur,i,j);
            }
            for(int j=1;j<=f[cur].tot;j++)f[cur].state[j]>>=2;
        }
        int ans=0;
        for(int i=1;i<=f[cur].tot;i++)ans=M(ans+f[cur].ans[i]);
        printf("%d",ans);
        return 0;
    }
    View Code

    经历:开始看错题,以为是向右向下延伸的方块,就1表示可以延伸的插头,2表示必须延伸的插头。

    但其实这也是不必要的,插头DP通常强制限定插头指向的方块必须选择,而在之前插的时候提前判断是否能选。

  • 相关阅读:
    # 漏洞挖掘技巧清单 #
    CVE-2020-0796——SMBV3客户端/服务端远程代码执行漏洞(远程代码执行+权限提升)
    SSH公钥详细介绍及拓展
    滑动窗口(数据结构)
    反素数解析
    树状数组的基本操作
    Rochambeau POJ
    一般图最大匹配
    2020牛客暑期多校训练营(第一场)H Minimum-cost Flow
    A Bug's Life POJ
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8817917.html
Copyright © 2011-2022 走看看