插头DP学习笔记

插头DP(我也不知道该怎么定义...)是一种类似于洛谷题目(【模板】插头DP)的题目
题目特征为:
- 在棋盘上
- 某一维的数据范围很小
- 完全铺满
- 计数问题
直接看题吧。
给出n*m的方格,有些格子不能铺线,其它格子必须铺,形成一个闭合回路。问有多少种铺法?(2<=n,m<=12)
考虑依次枚举每一个格子,存储轮廓线的状态。

例如图中我们当前正在考虑转移橙色格子,那么轮廓线就是图中的红线。
我们存储轮廓线上的插头。什么是插头呢?
例如下图

本图中的连线在轮廓线上的相交处就是所谓的插头。由于同一个连通块一定是联通的,在轮廓线上一定有两个交点,所以我们用左插头和右插头来表示这个插头是一个轮廓线的左侧交点还是右侧交点。显然两个连通块不可能相交,所以我们用括号序的形式来呈现左插头和右插头。如果我们以左插头为1,右插头为2,没有插头为0,则上图中的轮廓线插头状态是1201122。由于同一种方案在轮廓线上插头一定是一样的,而当前的转移至于轮廓线上的插头有关,符合DP的条件,所以我们可以存储以每一种轮廓线上插头的形式为下标,方案数为值的DP数组,依次考虑每一格子的插头形态,转移轮廓线。
注意为了后面讲解方便,我将连通块用同时用两种颜色标记,表示内部和外部。
显然这个格子的转移只与两条边有关:如图标记的两条黑边

根据这个格子附近的轮廓线情况,我们有若干种转移:
(以下图中轮廓线上方的插头情况仅供参考,实际可能有很多种情况,但是由于我们只考虑轮廓线上的插头,这几种情况本质上是一样的)
图太小了,凑合着看吧。。。
0.障碍节点
障碍节点不能防止插头,只有L=0&&R=0才能转移。
下面是非障碍节点的情况。
1.L=0, R=0

由于必须要放置插头,所以我们在下方放置一个左插头,右方放置一个右插头,即变成L=1 R=2
2.L=1, R=2

显然的是,我们可以补充一个左插头和一个右插头来形成一个回路。但是由于题目中要求只能形成一个闭合回路,所以如果当前考虑的节点是最后一个可以转移的非障碍节点,我们即可计入最终答案,否则不处理.
3.L=2, R=1

这次我们可以直接补充一个右插头和一个左插头来使得这两个回路形成一个回路。新的轮廓线上即变为L=0 R=0
4.L=1, R=1

显然我们需要把两个连通块连接在一起,但是我们需要更改和R连接的插头的方向,如图。我们找到和上方插头匹配的右插头,将其变更为左插头即可。实际写代码可能稍微麻烦一点。
5.L=2, R=2

和上一种情况类似,只是对称过来了,我们需要向左找和左方插头匹配的左插头,将其变更为右插头。
6.L!=0, R=0

我们只需要延伸这个插头:向下或向右,即L=原L,R=0或L=0,R=原L
7.L!=0, R=0

和上一种情况类似,我们只需要延伸插头。
然后我们可以写代码了。由于状压每个位置有3种状态,我们需要3进制。由于3进制太麻烦了,所以我们用4进制。所以状态编号复杂度为\(4^{12+1}\),大概六千万多,显然存不下,需要哈希表。这里偷懒使用unordered_map,在洛谷上不开O2刚好能过。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, m;
bool chenge[15][15];
char ch;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf(" %c", &ch), chenge[i][j] = (ch == '*');
unordered_map<int, long long> mp[2];
mp[0][0] = 1;
long long ans = 0;
int nn = -1, mm = -1;
for (int i = n; i >= 1; i--)
for (int j = m; j >= 1; j--)
if (chenge[i][j] != 1)
{
nn = i, mm = j;
goto fuck;
}
fuck:
if (nn == -1 && mm == -1)
{
printf("0\n");
return 0;
}
int tot = (1 << (2 * m)) - 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
mp[1].clear();
for (pair<const int, long long> &cjh : mp[0])
{
int s = cjh.first;
int L = (s >> (j * 2 - 2)) & 3, R = (s >> (j * 2)) & 3;
int src = s;
s ^= (L << (j * 2 - 2));
s ^= (R << (j * 2));
if (chenge[i][j] == 1)
{
if (L == 0 && R == 0)
mp[1][s] += cjh.second;
}
else
{
if (L == 0 && R == 0)
mp[1][s ^ (1 << (j * 2 - 2)) ^ (2 << (j * 2))] += cjh.second;
if (L == 1 && R == 2)
if (i == nn && j == mm && s == 0)
ans += cjh.second;
if (L == 2 && R == 1)
mp[1][s] += cjh.second;
if (L == 1 && R == 1)
{
int cnt = 0;
for (int pos = j; ; pos++)
{
if (((src >> (pos * 2)) & 3) == 1)
cnt++;
else if (((src >> (pos * 2)) & 3) == 2)
cnt--;
if (cnt == 0)
{
mp[1][s ^ (2 << (pos * 2)) ^ (1 << (pos * 2))] += cjh.second;
break;
}
}
}
if (L == 2 && R == 2)
{
int cnt = 0;
for (int pos = j - 1; ; pos--)
{
if (((src >> (pos * 2)) & 3) == 2)
cnt++;
else if (((src >> (pos * 2)) & 3) == 1)
cnt--;
if (cnt == 0)
{
mp[1][s ^ (2 << (pos * 2)) ^ (1 << (pos * 2))] += cjh.second;
break;
}
}
}
if ((L == 0 && R != 0) || (L != 0 && R == 0))
{
mp[1][s ^ ((L + R) << (j * 2 - 2))] += cjh.second;
mp[1][s ^ ((L + R) << (j * 2))] += cjh.second;
}
}
}
mp[0] = mp[1];
}
mp[1].clear();
for (pair<const int, long long> &cjh : mp[0])
if ((cjh.first & tot) == cjh.first)
mp[1][(cjh.first & tot) << 2] += cjh.second;
mp[0] = mp[1];
}
printf("%lld\n", ans);
return 0;
}
还有一道例题[SCOI2011]地板,先鸽了,过段时间再发
插头DP学习笔记的更多相关文章
- 插头DP学习笔记——从入门到……????
我们今天来学习插头DP??? BZOJ 2595:[Wc2008]游览计划 Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该 ...
- ural 1519 fomular 1 既插头DP学习笔记
直接看CDQ在2008年的论文吧. 个人认为她的论文有两个不明确的地方, 这里补充一下: 首先是轮廓的概念. 我们在进行插头DP时, 是从上往下, 从左往右逐个格子进行的, 已经处理的格子与未经处理的 ...
- 数位DP学习笔记
数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...
- DP学习笔记
DP学习笔记 可是记下来有什么用呢?我又不会 笨蛋你以后就会了 完全背包问题 先理解初始的DP方程: void solve() { for(int i=0;i<;i++) for(int j=0 ...
- 插头$DP$学习小结
插头\(DP\)学习小结 这种辣鸡毒瘤东西也能叫算法... 很优秀的一个算法. 最基本的适用范围主要是数据范围极小的网格图路径计数问题. 如果是像\(Noi2018\)那种的话建议考生在其他两道题难度 ...
- 树形DP 学习笔记
树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...
- 斜率优化DP学习笔记
先摆上学习的文章: orzzz:斜率优化dp学习 Accept:斜率优化DP 感谢dalao们的讲解,还是十分清晰的 斜率优化$DP$的本质是,通过转移的一些性质,避免枚举地得到最优转移 经典题:HD ...
- 动态 DP 学习笔记
不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...
- [总结] 动态DP学习笔记
学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...
随机推荐
- distinct可以用in代替(小技巧)
distinct可以用in代替,in的好处是直接能获取所有数据,而distunct只能获取distinct的字段,不过效率肯定高一些.
- [Chapter 3 Process]Practice 3.2 Including the initial parent process, how many processes are created by the program shown in Figure?
3.2 Including the initial parent process, how many processes are created by the program shown in Fig ...
- C#如何解析JSON数据(反序列化对象)
第一章:C#如何拿到从http上返回JSON数据? 第二章:C#如何解析JSON数据?(反序列化对象) 第三章:C#如何生成JSON字符串?(序列化对象) 第四章:C#如何生成JSON字符串提交给接口 ...
- C#封装CRUD到SqlHelper类解读
1.简单说明一下,一般情况下,数据库连接字符串是在App.config文件中进行配置,然后再在代码中进行引用.因此,我们在这里先看一下App.config文件. 首先看需要添加的内容: 参数说明: n ...
- awk简要使用
1 前言 awk是Unix环境下一种非常好的语言,适合于文本处理和报表生成,它还有许多精心设计的特性,允许进行特殊技巧程序设计.对于短消息来说,比如处理话单文件,使用awk就非常方便 ...
- mysql的安装以及简单的命令符
在百度当中输入mySQL就可以下载了. 我们只需要一路的点击next就好了,注意,我们在安装的过程当中它会问我们是否要安装路径,我么要选择是. 在显示安装完成之后呢,我们会看到一个复选框,上面写着是否 ...
- Maven学习入门——2016-2-17
一.Maven的基本概念 1.1Mawen是干啥的??? 我们第一次接触Maven一般就是用Maven为我们的项目加入jar包,非常的方便. maven到底是干什么的??说白了,maven就是用来管理 ...
- 数据库理论-范式(1NF、2NF、3NF)
范式是“符合某一种级别的关系模式的集合,表示一个关系内部各属性之间的联系的合理化程度”. 第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项.(每个属性不可分割)第二范式(2NF)要求数据 ...
- 开发工具 idea 激活方法
1. 到网站 http://idea.lanyus.com/ 获取注册码. 2.填入下面的license server: http://intellij.mandroid.cn/ http://ide ...
- 我们为什么要在C++中使用虚函数
类中的成员函数分为静态成员函数和非静态成员函数,而非静态成员函数又分为普通函数和虚函数. 至于为什么虚函数必须是非静态成员函数,可以看一下:http://blog.csdn.net/leiming32 ...