插头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)\) 的做法就 ...
随机推荐
- 【转】gem install libv8 错误
转自:http://my.oschina.net/moks/blog/200344 [摘要]Because libv8 is the interface for the V8 engine used ...
- Ettercap进行arp毒化
攻击者IP:192.168.220.152 受害者IP:192.168.220.151 网关:192.168.220.2 修改DNS文件 ┌─[root@sch01ar]─[~] └──╼ #vim ...
- Cookies和Session的定义与区别
Cookies和Session二者的定义: 当你在浏览网站的时候,WEB 服务器会先送一小小资料放在你的计算机上,Cookie 会帮你在网站上所打的文字或是一些选择,都纪录下来.当下次你再光临同一个网 ...
- maven中pom.xml元素含义
- Tornado之抽屉实战(2)--数据库表设计
经过我们上次分析,数据库要有最基本的四张表,用户表,消息表,类型表,点赞表,评论表,接下来我们看着怎么设计吧 首先我们要清楚,表设计的代码是写在models下的 用户表 ? 1 2 3 4 5 6 7 ...
- GitHub Blog创建以及本地管理(转)
GitHub Blog创建以及本地管理 创建 注册GitHub账户 首页点击新建仓库 New repository repository name必须为 Owner.github.io EX:我的 ...
- 2018多校第九场1010 (HDU6424) 数学
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6424 解法:找规律.因为最多三项,a1^a2^a3可以拆成(a1+2)+(a2+1)*a3,然后建成数 ...
- Data Mining: SSE,MSE,RMSE,R-square指标讲解
转载自:http://blog.csdn.net/l18930738887/article/details/50629409 SSE(和方差.误差平方和):The sum of squares due ...
- Linux之tcpdump使用详解
1.1 三种关键字 关于类型的关键字 第一种是关于类型的关键字,主要包括host,net,port, 例如 host 210.27.48.2,指明 210.27.48.2是一台主机,net 202. ...
- Netty的Channel
Channel是一个网络端口连接,或者是可以进行读,写,链接,绑定端口的组件的连接. Channel就是一个链接,它提供了如下的功能. 1:获取当前链接的状态 2:配置当前链接参数 3:进行read ...