插头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)\) 的做法就 ...
随机推荐
- android 定位的几种方式介绍
[地理位置] android 定位的几种方式介绍 开发中对于地图及地理位置的定位是我们经常要用地,地图功能的使用使得我们应用功能更加完善,下面 www.androidkaifa.com 总结了一下网络 ...
- 将CDM中所有以Relatonship_开头的关系全部重命名,避免生成数据库因为重复关系名报错
Option Explicit ValidationMode = True InteractiveMode = im_Batch Dim mdl '当前model '获取当前活 ...
- PowerDesigner CDM中取消默认不能存在同名主键的方法
This data item is already used in a primary identifier.Normalization rules prevent ... 处理的方法为: 菜单栏上的 ...
- svn 撤销修改
1,更新到最新版本 不知道是哪个文件的问题时,可以svn log查看日志,基本上可以通过哪一次提交修改了哪一个文件推算出 是哪个文件的问题. 若知道是哪个文件的问题,直接svn log 文件名 大致确 ...
- Solaris10技巧
如何查看UFS文件系统创建命令 root@ofs0accmcc01 # mkfs -m /dev/md/rdsk/d100 mkfs -F ufs -o nsect=128,ntrack=48,bsi ...
- 问题:oracle LISTAGG 连接字符串;结果:Oracle 连接字符串的方法
Oracle 连接字符串的方法 方法一:wmsys.wm_concat(column) 介绍:其函数在Oracle 10g推出,在10g版本中,返回字符串类型,在11g版本中返回clob类型.括号里面 ...
- mysql权限表
http://blog.csdn.net/zhangliangzi/article/details/51882710
- [Python Study Notes]水平柱状图绘制
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...
- new与delete,malloc与free
1.new_delete与malloc_free ❶malloc/free: malloc原型: void *malloc(long NumBytes) 该函数分配了NumBytes个字节,并返回了指 ...
- [P3812][模板]线性基
解题关键:求异或最大值.线性基模板题. 极大线性无关组的概念. 异或的值域相同. #include<cstdio> #include<cstring> #include< ...