【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;
}

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

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

原文地址:https://www.cnblogs.com/onioncyc/p/8817917.html

时间: 2024-10-10 03:54:54

【BZOJ】2331: [SCOI2011]地板 插头DP的相关文章

bzoj 2331: [SCOI2011]地板 插头dp

用四进制表示状态. 用hash表把一个四进制数映射到一个小数上. 这样就可以memset了. 转移的时候分类讨论一下,特判下边界情况. 1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 #define bq 1<<bit[j] 6 #define bp 1<<bit[j-1] 7 using namespace std

2331: [SCOI2011]地板 插头DP

国际惯例的题面:十分显然的插头DP.由于R*C<=100,所以min(R,C)<=10,然后就可以愉悦地状压啦.我们用三进制状压,0表示没有插头,1表示有一个必须延伸至少一格且拐弯的插头,2表示有一个必须延伸一格且不可以拐弯的插头.转移的话就十分显然了.00->22,表示用这个格子作为开始的拐角.00->10,表示用这个格子向下延伸.00->01,表示用这个格子向右延伸.01->10,表示这个格子连接上下.01->02,表示在这个格子作为中间的拐角.02->

【BZOJ2331】[SCOI2011]地板 插头DP

[BZOJ2331][SCOI2011]地板 Description lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西.小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板.现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案? 需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0.铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次. Input 输入的第一行包含两个整数,R和C,表示客厅

[入门向选讲] 插头DP:从零概念到入门 (例题:HDU1693 COGS1283 BZOJ2310 BZOJ2331)

转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7326874.html 最近搞了一下插头DP的基础知识……这真的是一种很锻炼人的题型…… 每一道题的状态都不一样,并且有不少的分类讨论,让插头DP十分锻炼思维的全面性和严谨性. 下面我们一起来学习插头DP的内容吧! 插头DP主要用来处理一系列基于连通性状态压缩的动态规划问题,处理的具体问题有很多种,并且一般数据规模较小. 由于棋盘有很特殊的结构,使得它可以与“连通性”有很强的联系,因此插头DP最常见的应用要数

【BZOJ 2331】 [SCOI2011]地板

2331: [SCOI2011]地板 Time Limit: 5 Sec  Memory Limit: 128 MB Submit: 598  Solved: 264 [Submit][Status][Discuss] Description lxhgww的小名叫"小L",这是因为他总是很喜欢L型的东西.小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板.现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案? 需要注意的是,如下图所示,

bzoj2331 : [SCOI2011]地板 2011-12-20

2331: [SCOI2011]地板Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 128  Solved: 54[Submit][Status][Discuss]Description lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西.小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板.现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案? 需要注意的是,如下图所示,L型地板的两端长度可

BZOJ 2331 地板(插头DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2331 题意:给出一个n*m的地面.有些是障碍.用L型的地板砖铺满.有多少种方案. 思路:用0表示没有插头,用1表示有插头且可以拐弯,用3表示有插头但是不能再拐弯了. 设有m列,轮廓线为[0,m].对于格子(i,j),设左插头x上插头y,那么转移有: (1)x=0,y=0:此时如图1-0,有三种转移,分别是1-1,1-2,1-3; (2)x!=0,y!=0:此时只有当x=y=1时可以转移

P3272 [SCOI2011]地板(插头DP)

[题面链接] https://www.luogu.org/problemnew/show/P3272 [题目描述] 有一个矩阵,有些点必须放,有些点不能放,用一些L型的图形放满,求方案数 [题解] (版权所有) https://www.luogu.org/blog/dedicatus545/solution-p3272 第一种情况:当前状态下,当前格子上方和左方都没有插头 这时我们需要找一个L形来把这个格子填上,那么我们可能有三种决策: 决策一:给这个格子加一个二号下插头和一个二号右插头,此时这

BZOJ 2595 游览计划(插头DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2595 题意:给出一个数字矩阵.求一个连通块包含所有的数字0且连通块内所有数字之和最小. 思路:对于每个格子,是0则必须要选.那么对于不选的格子(i,j)在什么时候可以不选呢?必须同时满足以下两个条件: (1)(i,j)不是0: (2)(i-1,j)不选或者(i-1,j)选了但是轮廓线上还有别的地方与(i-1,j)是一个连通块. int Pre[105][N],op[105][N]; s