P3272 [SCOI2011]地板(插头DP)
[题面链接] https://www.luogu.org/problemnew/show/P3272
[题目描述] 有一个矩阵,有些点必须放,有些点不能放,用一些L型的图形放满,求方案数
[题解] (版权所有) https://www.luogu.org/blog/dedicatus545/solution-p3272
第一种情况:当前状态下,当前格子上方和左方都没有插头
这时我们需要找一个L形来把这个格子填上,那么我们可能有三种决策:
决策一:给这个格子加一个二号下插头和一个二号右插头,此时这个格子是一个新的L形的拐角
决策二:给这个格子加一个一号下插头,此时相当于构建了一个先向下再向右的L形,从当前格子开始
决策三:给这个格子加一个一号右插头,此时相当于构建了一个先向右再向下的L形,从当前格子开始
第二种情况:当前状态下,当前格子上方有一个一号下插头,左方没有插头
这时相当于上面有一个L形的未拐弯的一边伸过来了,有两种决策:
决策一:L形不拐弯,继续向下延伸,当前格子只有一个一号下插头
决策二:L形在当前格子拐弯,L形向右延伸,当前格子只有一个二号右插头
第三种情况:当前状态下,当前格子左方有一个一号右插头,上方没有插头
与第二种情况类似,故不赘述
第四种情况:当前状态下,当前格子上方有一个二号下插头,左方没有插头
这时相当于上面有一个L形的拐过弯的一边伸过来了,有两种决策:
决策一:L形继续延伸,当前格子有一个二号下插头
决策二:L形在当前格子终止,当前格子没有插头
决策二时注意,如果当前处在最后一个没有障碍的格子,那么需要统计入最终答案
第五种情况:当前状态下,当前格子左方有一个二号右插头,上方没有插头
与第四种情况类似,故不赘述
第六种情况:当前状态下,当前格子左方和上方都有一号插头
此时相当于两条“臂”伸了过来,在当前格子相交,应该合并成一个L形
当前格子相当于一个L形的拐弯处,没有插头
我们发现,情况一和情况六已经覆盖了四种L形的摆放方式,同时也不会有一个二号插头一个一号插头或者两个二号插头的情况出现——它们被上文的六种情况限制了
故这种分类讨论方式可以覆盖所有情况
总结
\(1.\) 插头定义在扫描线上方,定义处理前插头在左上的上方,处理完当前点后对应的扫描线图形后插头在右下的上方
\(2.\) 插头DP神题,注意分类讨论,一般分类标准为向右向下延伸,而向左向上的情况则通过在某一个点合并上面和左边传来的
//  https://www.luogu.org/blog/dedicatus545/solution-p3272
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define hash deep_dark_fantasy
#define ll long long
#define MOD 20110520
using namespace std;
inline int read(){
    int re=0,flag=1;char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
int n,m,x[150][150],cur,pre,ex,ey;
int st[2][300010];ll ans[2][300010],re;
int tot[2],bit[20],state[300010],st_tot,hash=300000;
struct edge{
    int to,next;
}a[300010];
void insert(int now,ll val){
    int p=now%hash;
    for(int i=state[p];i;i=a[i].next){
        if(st[cur][a[i].to]==now){
            ans[cur][a[i].to]+=val;
            ans[cur][a[i].to]%=MOD;return;
        }
    }
    tot[cur]++;
    a[++st_tot].to=tot[cur];
    a[st_tot].next=state[p];
    state[p]=st_tot;st[cur][tot[cur]]=now;ans[cur][tot[cur]]=val%MOD;
}
void dp(){
    int i,j,k,down,right,now;ll val;
    cur=0;tot[cur]=1;ans[cur][1]=1;st[cur][1]=0;
    for(i=1;i<=n;i++){
        for(j=1;j<=tot[cur];j++) st[cur][j]<<=2;
        for(j=1;j<=m;j++){
            memset(state,0,sizeof(state));st_tot=0;
            pre=cur;cur^=1;tot[cur]=0;
            for(k=1;k<=tot[pre];k++){
                now=st[pre][k];val=ans[pre][k];
                right=(now>>bit[j-1])%4;down=(now>>bit[j])%4;
                if(!x[i][j]){//障碍格子
                    if(!down&&!right){
                        insert(now,val);continue;
                    }
                }
                if(!right&&!down){//第一种情况
                    if(x[i+1][j]&&x[i][j+1])
                        insert(now+((1<<bit[j-1])<<1)+((1<<bit[j])<<1),val);
                    if(x[i+1][j]) insert(now+(1<<bit[j-1]),val);
                    if(x[i][j+1]) insert(now+(1<<bit[j]),val);
                }
                if(right==1&&!down){//第三种情况
                    if(x[i][j+1]) insert(now-(1<<bit[j-1])+(1<<bit[j]),val);
                    if(x[i+1][j]) insert(now+(1<<bit[j-1]),val);
                }
                if(down==1&&!right){//第二种情况
                    if(x[i+1][j]) insert(now-(1<<bit[j])+(1<<bit[j-1]),val);
                    if(x[i][j+1]) insert(now+(1<<bit[j]),val);
                }
                if(right==2&&!down){//第五种情况
                    if(i==ex&&j==ey) re+=val,re%=MOD;
                    if(x[i][j+1]) insert(now-((1<<bit[j-1])<<1)+((1<<bit[j])<<1),val);
                    insert(now-((1<<bit[j-1])<<1),val);
                }
                if(down==2&&!right){//第四种情况
                    if(i==ex&&j==ey) re+=val,re%=MOD;
                    if(x[i+1][j]) insert(now-((1<<bit[j])<<1)+((1<<bit[j-1])<<1),val);
                    insert(now-((1<<bit[j])<<1),val);
                }
                if(down==1&&right==1){//第六种情况
                    if(i==ex&&j==ey) re+=val,re%=MOD;
                    insert(now-(1<<bit[j-1])-(1<<bit[j]),val);
                }
            }
        }
    }
}
int main(){//这道题的读入没有保证n>=m,所以我写了一个判断,来保证n>=m,具体操作就是把图旋转了九十度
    int i,j;char ch;
    n=read();m=read();
    for(i=1;i<=20;i++) bit[i]=i<<1;
    if(n>m){
        for(i=1;i<=n;i++){
            for(j=1;j<=m;j++){
                ch=getchar();
                while(ch!='*'&&ch!='_') ch=getchar();
                x[i][j]=(ch=='_');
                if(x[i][j]) ex=i,ey=j;
            }
        }
    }
    else{
        swap(n,m);
        for(i=m;i>0;i--){
            for(j=1;j<=n;j++){
                ch=getchar();
                while(ch!='*'&&ch!='_') ch=getchar();
                x[j][i]=(ch=='_');
                if(x[j][i]&&((j>ex)||(j==ex&&i>ey))) ex=j,ey=i;
            }
        }
    }
    dp();
    printf("%lld",re);
}
P3272 [SCOI2011]地板(插头DP)的更多相关文章
- bzoj 2331: [SCOI2011]地板 插头DP
		2331: [SCOI2011]地板 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 541 Solved: 239[Submit][Status] D ... 
- 【BZOJ2331】[SCOI2011]地板 插头DP
		[BZOJ2331][SCOI2011]地板 Description lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西.小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里 ... 
- 2331: [SCOI2011]地板 插头DP
		国际惯例的题面:十分显然的插头DP.由于R*C<=100,所以min(R,C)<=10,然后就可以愉悦地状压啦.我们用三进制状压,0表示没有插头,1表示有一个必须延伸至少一格且拐弯的插头, ... 
- 【BZOJ】2331: [SCOI2011]地板 插头DP
		[题意]给定n*m的地板,有一些障碍格,要求用L型的方块不重不漏填满的方案数.L型方块是从一个方格向任意两个相邻方向延伸的方块,不能不延伸.n*m<=100. [算法]插头DP [题解]状态0表 ... 
- BZOJ 2331 [SCOI2011]地板 ——插头DP
		[题目分析] 经典题目,插头DP. switch 套 switch 代码瞬间清爽了. [代码] #include <cstdio> #include <cstring> #in ... 
- [SCOI2011][bzoj2331] 地板 [插头dp]
		题面: 传送门 思路: 插头dp基础教程 这个L形......第一眼看上去真的是丧病啊 但是仔细想想,实际上也就是拿一堆路径铺满一个棋盘,这个路径还是有限制的 那还有什么好说的,插头dp上啊[雾] 首 ... 
- P3272 [SCOI2011]地板
		\(\color{#0066ff}{ 题目描述 }\) lxhgww的小名叫"小L",这是因为他总是很喜欢L型的东西.小L家的客厅是一个R*C的矩形,现在他想用L型的地板来铺满整个 ... 
- 洛谷P3272 [SCOI2011]地板(插头dp)
		传送门 感谢大佬的教导->这里 容易注意到,本题的合法路径“L型地板”有一些特殊的地方:拐弯且仅拐弯一次. 这由于一条路径只有两种状态:拐弯过和没拐弯过,因此我们可以尝试着这样定义新的插头: 我 ... 
- 插头dp小结
		插头dp: \(A:\)插头dp是什么? \(B:\)一种基于连通性状态压缩的动态规划问题 \(A:\)请问有什么应用呢? \(B:\)各种网格覆盖问题,范围允许状压解决,常用于计算方案数与联通块权值 ... 
随机推荐
- Shell编程进阶  1.6 if判断的几种用法
			针对文件和目录的逻辑判断 touch .txt .txt ]; then echo ok;fi -f 判断1.txt是否是文件且是否存在,成立输出ok if [-d /tmp/ ]; then ech ... 
- oracle常用函数总结(一)
			最近在读数据库存储过程,或者在xml里写sql时用到数据库函数,笔者觉得有必要总结一下,当然有的函数笔者也很懵逼,不过可以问度娘啊!好了!开始正题. )s from dual;--1 若nvl第一个参 ... 
- Reporting services
			“数据库引擎服务”可以承载报表服务器数据库.Reporting Services 需要SQL Server 2008 数据库引擎的本地或远程实例来承载报表服务器数据库.如果同时安装数据库引擎实例和 R ... 
- Java开源中文分词类库
			IKAnalyzer IKAnalyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包.从2006年12月推出1.0版开始,IKAnalyzer已经推出了3个大版本.最初,它是以开 ... 
- [cerc2017J]Justified Jungle
			题目大意:删去k条边,树变为相等个点的连通分量,求所有正整数k. 解题关键:树dp,不必求因子. #include<bits/stdc++.h> using namespace std; ... 
- newcoder中的基础题
			1. mysql_num_fields() 函数返回结果集中字段的数 2. <?php class A{ ; } $a = new A(); $b = $a; $a; echo $b-> ... 
- C++实现数组的排序/插入重新排序/以及逆置操作
			插入新的数字重新排序 分析:将新的数字与已经排序好的数组中的数字一一比较,直到找到插入点,然后将插入点以后的数字都向后移动一个单位(a[i+1]=a[i]),然后将数据插入即可. 代码: #inclu ... 
- MSER
			1.注释很全的分析:http://blog.csdn.net/zhaocj/article/details/40742191 2.opencv采用的mser实现方法 * 1. the gray ima ... 
- 为什么不推荐用破解版的winrar
			站在winrar公司的角度,作为winrar的开发商或者运营商当然不希望用户使用破解版的winrar,因为这损害了他们的利益,这是屁股问题. 站在用户的角度,我希望免费使用世界上所有的软件.但这个世界 ... 
- c++ 子类切勿重新定义父类 non-virtual函数
			子类如果重新定义了父类的non-virtual函数则不会有多态效果. 为方便描述,这里的方法和继承方式都用public.至于原因嘛,你懂的! 1:子类直接继承父类方法 class BaseOption ... 
