博客
关于我
插头DP学习笔记
阅读量:435 次
发布时间:2019-03-06

本文共 2792 字,大约阅读时间需要 9 分钟。

插头DP学习笔记

用途

有些 状压 \(DP\) 问题要求我们记录状态的连通性信息,这类问题一般被形象的称为插头 \(DP\) 或连通性状态压缩 \(DP\)

例如格点图的哈密顿路径计数,求棋盘的黑白染色方案满足相同颜色之间形成一个连通块的方案数,以及特定图的生成树计数等等。

这些问题通常需要我们对状态的连通性进行编码,讨论状态转移过程中连通性的变化。

例题

首先要明确两个概念:

轮廓线:已决策状态和未决策状态的分界线。

插头:一个格子某个方向的插头存在,表示这个格子在这个方向与相邻格子相连。

我们要状压的就是轮廓线上插头的状态。

具体来说,可以把路径的合并看作括号的匹配。

一般的状压 \(dp\) 只有 \(0,1\) 两个状态,分别表示有和没有,但是这道题需要用三进制的状态压缩,\(0,1,2\) 分别表示没有括号,有左括号,有右括号。

之所以要分左右括号是因为要区分下面这两种情况:

第一种情况中间的两个括号是不能合并的,因为要恰好形成一条回路,第二种情况则能够合并。

一条轮廓线会由 \(n+1\) 条线段组成,其中 \(n\) 条是左右方向的,另外 \(1\) 条是上下方向的。

为了方便解压状态,我们用四进制来表示,同时减少枚举的状态,要把所有的状态存到哈希表里。

转移的时候大力分类讨论:

\(1\)、当前的位置不能有路径经过

如果没有向右的插头或者向下的插头,直接继承上一个格子的答案,

否则不存在合法的方案。

if(s[i][j]=='*'){	if(!r && !dow) f[now].ad(nzt,nval);}

\(2\)、当前的位置必须经过并且没有向右的插头或者向下的插头。

需要在当前的格子新开一个向右的插头和向下的插头,并且把向右的插头标记为右括号,把向下的插头标记为左括号。

我在转移状态之前就去判断这个状态是否合法,这样会比较好写。

else if(!r && !dow){	if(s[i][j+1]=='.' && s[i+1][j]=='.') f[now].ad(nzt|2|(1<

\(3\)、当前的位置必须经过并且只有向右的插头或者向下的插头。

可以继续沿着之前的方向或者改变插头的方向,左右括号不变。

else if(r && !dow){	if(s[i][j+1]=='.') f[now].ad(nzt,nval);	if(s[i+1][j]=='.') f[now].ad(nzt^r|(r<

\(4\)、当前的位置必须经过并且有一个代表左括号的右插头和一个代表右括号的下插头。

如果当前的点是图中右下角的终止节点并且不存在其它匹配的括号更新答案。

else if(r==1 && dow==2){	if(i==edx && j==edy && (nzt^r^(dow<

\(5\)、当前的位置必须经过并且有一个代表左括号的下插头和一个代表右括号的右插头。

将这两个括号匹配。

else if(r==2 && dow==1){	f[now].ad(nzt^r^(dow<

\(6\)、当前的位置必须经过并且有一个下插头和一个右插头,并且这两个插头都代表左括号。

一直向右找,找到第一个左括号和右括号恰好匹配的位置,把这个位置的右括号改为左括号,之前的两个左括号直接匹配。

else if(r==1 && dow==1){	cs1=nzt^r^(dow<
>o*2&3; p+=(cs2==1)-(cs2==2); if(!p){ cs1^=3<

\(7\)、当前的位置必须经过并且有一个下插头和一个右插头,并且这两个插头都代表右括号。

和上面的情况一样,但是需要改成向左找。

else if(r==2 && dow==2){	cs1=nzt^r^(dow<
>o*2&3; p+=(cs2==2)-(cs2==1); if(!p){ cs1^=3<

代码

#include
#include
#include
#include
#include
#define rg registerconst int maxn=14,mod=1e5+3,maxm=1e5+5;int n,m,edx,edy;char s[maxn][maxn];long long ans=0;struct has{ struct asd{ int nxt,zt; long long val; }b[maxm]; has(){ memset(h,-1,sizeof(h)); tot=1; } int tot,h[maxm]; void cls(){ memset(h,-1,sizeof(h)); tot=1; } void ad(rg int zt,rg long long val){ rg int now=zt%mod; for(rg int i=h[now];i!=-1;i=b[i].nxt){ if(b[i].zt==zt){ b[i].val+=val; return; } } b[tot].val=val; b[tot].zt=zt; b[tot].nxt=h[now]; h[now]=tot++; }}f[2];int main(){ scanf("%d%d",&n,&m); for(rg int i=1;i<=n;i++){ scanf("%s",s[i]+1); for(rg int j=1;j<=m;j++){ if(s[i][j]=='.') edx=i,edy=j; } } rg int now=0,nzt,r,dow,cs1,cs2; rg long long nval; f[0].ad(0,1); for(rg int i=1;i<=n;i++){ for(rg int j=1;j<=m;j++){ now^=1; f[now].cls(); for(rg int k=1;k
>j*2&3; if(s[i][j]=='*'){ if(!r && !dow) f[now].ad(nzt,nval); } else if(!r && !dow){ if(s[i][j+1]=='.' && s[i+1][j]=='.') f[now].ad(nzt|2|(1<
>o*2&3; p+=(cs2==1)-(cs2==2); if(!p){ cs1^=3<
>o*2&3; p+=(cs2==2)-(cs2==1); if(!p){ cs1^=3<

转载地址:http://yyvyz.baihongyu.com/

你可能感兴趣的文章
自定义拦截器
查看>>
Eclipse 代码规范配置
查看>>
Kafka Producer机制优化-提高发送消息可靠性
查看>>
面试题5:(事务管理) ACID 是什么?
查看>>
ASCII码表
查看>>
剑指 Offer 20. 表示数值的字符串
查看>>
10.Mybatis执行流程
查看>>
【车间调度】遗传算法求解混合流水车间调度最优问题【Matlab 017期】
查看>>
Oracle 一张表里面按照一个字段值将所有的数据按逗号拆分,变为多行数据
查看>>
DRF框架(十四)——过滤Filtering,排序
查看>>
【ucosII】4.事件管理
查看>>
【ucosII】5.消息队列
查看>>
阿里云网盘注册邀请码怎么获得,阿里云网盘注册邀请码获得内测方法
查看>>
Jmeter函数与变量使用详解(下)-32
查看>>
数模新版视频课程第5讲.相关系数
查看>>
ie盒模型与标准盒模型下的设置颜色区域的宽度
查看>>
js 各循环的区别
查看>>
linux 基础-变量,shell基本语法
查看>>
opencv图像处理学习(六十)——系统函数
查看>>
Qt5模块功能介绍
查看>>