插头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学习笔记的更多相关文章

  1. 插头DP学习笔记——从入门到……????

    我们今天来学习插头DP??? BZOJ 2595:[Wc2008]游览计划 Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该 ...

  2. ural 1519 fomular 1 既插头DP学习笔记

    直接看CDQ在2008年的论文吧. 个人认为她的论文有两个不明确的地方, 这里补充一下: 首先是轮廓的概念. 我们在进行插头DP时, 是从上往下, 从左往右逐个格子进行的, 已经处理的格子与未经处理的 ...

  3. 数位DP学习笔记

    数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...

  4. DP学习笔记

    DP学习笔记 可是记下来有什么用呢?我又不会 笨蛋你以后就会了 完全背包问题 先理解初始的DP方程: void solve() { for(int i=0;i<;i++) for(int j=0 ...

  5. 插头$DP$学习小结

    插头\(DP\)学习小结 这种辣鸡毒瘤东西也能叫算法... 很优秀的一个算法. 最基本的适用范围主要是数据范围极小的网格图路径计数问题. 如果是像\(Noi2018\)那种的话建议考生在其他两道题难度 ...

  6. 树形DP 学习笔记

    树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...

  7. 斜率优化DP学习笔记

    先摆上学习的文章: orzzz:斜率优化dp学习 Accept:斜率优化DP 感谢dalao们的讲解,还是十分清晰的 斜率优化$DP$的本质是,通过转移的一些性质,避免枚举地得到最优转移 经典题:HD ...

  8. 动态 DP 学习笔记

    不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...

  9. [总结] 动态DP学习笔记

    学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...

随机推荐

  1. c语言-单链表(一)

    定义节点: typedef struct Node { int data; Node* pNext; }NODE, *PNODE; 细节说明,PNode 就代表struct Node* ,上面的表单是 ...

  2. c#在sql中存取图片image示例

    这篇文章主要介绍了c#在sql中存取图片image示例,需要的朋友可以参考下 (1)控制台应用程序下演示插入图片 复制代码 代码如下: public void InsertIMG() { //将需要存 ...

  3. java 多线程系列---JUC原子类(一)之框架

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: AtomicIn ...

  4. 解决springMVC文件上传报错: The current request is not a multipart request

    转自:https://blog.csdn.net/HaHa_Sir/article/details/79131607 解决springMVC文件上传报错: The current request is ...

  5. IDEA中Git实战

    工作中多人使用版本控制软件协作开发,常见的应用场景归纳如下: 假设小组中有两个人,组长小张,组员小袁 场景一:小张创建项目并提交到远程Git仓库 场景二:小袁从远程Git仓库上获取项目源码 场景三:小 ...

  6. [Python Study Notes]pd.read_csv()函数读取csv文件绘图

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ...

  7. TCP/IP 笔记 1.1 概 述

    四个层次 每一层负责不同的功能:1) 链路层,有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡.它们一起处理与电缆(或其他任何传输媒介)的物理接口细节.2) ...

  8. android下db-journal文件作用

    在学习数据库sqlite的过程中,发现在源文件包里除了生成db类型的数据库文件,还生成了db-journal类型的同名文件 查询网上资料后知道该文件是sqlite的一个临时的日志文件,主要用于sqli ...

  9. 利用JavaScriptCore实现简单的功能(阶乘)

    #import "RootViewController.h" #import <JavaScriptCore/JavaScriptCore.h> @interface ...

  10. sleep()和usleep()

    函数名: sleep头文件: #include <windows.h> // 在VC中使用带上头文件        #include <unistd.h>  // 在gcc编译 ...