好学易懂 从零开始的插头DP(一)
好学易懂 从零开始的插头DP(一)
写在前面
这是一篇,以蒟蒻视角展开的梳理总结。更改了一些顺序,变化了一些细节。方便蒟蒻学习理解(起码本蒟蒻是这样)。大佬们可以直接看其它大佬的博客,可以学的更快。
你必须要学会的前置知识:状态压缩DP
学不会依旧可以读,但是推荐学的前置知识:哈希
论文贡前面,建议读完博客再看。
《基于连通性状态压缩的动态规划问题》
什么是插头DP
很显然,是一个关于插头的动态规划。那么,什么是插头呢?
如图我们在一个方格内,关于格点画一条闭合回路。
对于每一个方格,内部,有六种情况
不难发现,对于回路里的任何一个方格,四条边中,有且仅有两个与表示路径的蓝色线相交。这也很好理解,进一次,出一次,C(4,2)=6。
我们现在把格子里的蓝色线条,变成从格子中心指向外边的→。
这个箭头,也就是所谓的插头。
例题
我们结合一个例题来看,这个题目是洛谷模板题的弱化版,很多博客放在了模板题后的第一题,结合个人经历我觉得它比模板题更适合作第一题。
题目链接:HDU1693 or 洛谷P5074
题目大意:给一个n*m的方格,有些格子必须走,有些格子不能走。问有多少种不同的闭合回路。(1<=n,m<=12)
那么,把回路模型变成插头模型有什么好处或者性质呢?
1:首先,我们可以发现,如果一个格子上方的格子有下插头,那这个格子一定有上插头。其它方向类似。
2:一个格子的合理取法合且仅合相邻的格子有关。
观察下第二点,它其实代表了无后效性。假设我们从上到下,从左到右的处理每一个格子,那么我们只需要记录部分格子的状态即可,再往上的格子具体状态不用知道。
如上图,对于当前格子,我们只需要知道红色的这些格子就行了,再上面的格子具体的取法,已经不会对下面任何未处理的格子产生影响。
已经掌握了状态压缩的你,一定能轻松的算出状态总数,每个格子6种,维护n个格子。总共6 n 6^n6n种状态,好的,完蛋,只有2e9个状态。
别急,我们真的需要2e9个状态嘛?这些格子里,指向彼此和已经处理过的格子的插头,显然是废物信息。我们实际上只需要知道这些插头嘛:
蓝色的是其它格子需要用到的,黄色的是当前格子需要用到的。我们只需要知道这m+1个箭头是否存在就可以了。总共2 m ∗ 2 2^m*22m∗2个状态。再乘上n和m,时空复杂度都绰绰有余。
那么,怎么实现呢?我们要解决两个问题。
1:已知这些插头的情况下,这个方格该如何填。
2:填完这个方格后,如何得到下一个方格所需要的插头状态,更特殊的,如何从上一行行末,变到下一行行初。
这两个问题,其实都不是很难,稍微思考下,都可以独立解答,建议思考后再往下看。图片挡下文大法。
问题1:
1:如果当前格子存在左侧插头和上方插头,那么只有一种合理填法。
2:如果仅存在左侧插头,那么有两种合理填法。

3:如果仅存在上方插头,那和上一种类似,也是两种填法。

4:如果都不存在呢?只有一种填法
问题2:
解答了问题1,显然我们也得到了问题2的解答,毕竟我们填出了这个格子,自然知道插头分布。唯一特殊的是上一行末到这一行头的处理。上一行末不可能有右插头,那我们直接把上一行末状态的表示最后是否存在右插头的位置去掉,再添加一个表示没有左插头的位,不就表示出了这一行第一个的状态了嘛,为了方便写,下方的代码里,我用dp[i][0][mask]表示转移后的上一行行末状态。
到这里,我们已经得到了解法了,成熟的评测机,应该自动AC了吧(划去)。插头DP还是要多写的,千万自己写一遍,别忘了,这只是模板题的弱化。
这里提供一份代码(洛谷AC)

1 #include<iostream>
2 #include<stdio.h>
3 #include<cstring>
4 using namespace std;
5 int n,m,maxk,a[13][13];
6 long long dp[13][13][1<<14];
7 void init()
8 {
9 scanf("%d%d",&n,&m);
10 maxk=(1<<(m+1))-1;
11 for (int i=1;i<=n;i++)
12 {
13 for (int j=1;j<=m;j++)
14 {
15 scanf("%d",&a[i][j]);
16 }
17 }
18 memset(dp,0,sizeof(dp));
19 }
20 void solve()
21 {
22 int prei,prej;
23 dp[0][m][0]=1;
24 for (int i=1;i<=n;i++)
25 {
26 for (int k=0;k<=maxk;k++)
27 {
28 dp[i][0][k<<1]=dp[i-1][m][k];
29 }
30 for (int j=1;j<=m;j++)
31 {
32 prei=i;
33 prej=j-1;
34 for (int k=0;k<=maxk;k++)
35 {
36 int b1=(k>>(j-1))&1;
37 int b2=(k>>j)&1;
38 if (!a[i][j])
39 {
40 if (!b1&&!b2) dp[i][j][k]+=dp[prei][prej][k];
41 }
42 else if (!b1&&!b2)
43 {
44 dp[i][j][k+(1<<j)+(1<<(j-1))]+=dp[prei][prej][k];
45 }
46 else if (b1&&!b2)
47 {
48 dp[i][j][k]+=dp[prei][prej][k];
49 dp[i][j][k+(1<<(j-1))]+=dp[prei][prej][k];
50 }
51 else if (!b1&&b2)
52 {
53 dp[i][j][k]+=dp[prei][prej][k];
54 dp[i][j][k-(1<<(j-1))]+=dp[prei][prej][k];
55 }
56 else if (b1&&b2)
57 {
58 dp[i][j][k-(1<<j)-(1<<(j-1))]+=dp[prei][prej][k];
59 }
60 }
61 }
62 }
63 printf("%lld\n",dp[n][m][0]);
64 }
65 int main()
66 {
67 int t;
68 scanf("%d",&t);
69 while (t--)
70 {
71 init();
72 solve();
73 }
74 return 0;
75 }
of P5074
电脑前这个努力的帅气身影是谁呢?そう 私 です
好学易懂 从零开始的插头DP(一)的更多相关文章
- 插头dp
插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...
- HDU 4113 Construct the Great Wall(插头dp)
好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...
- HDU 4949 Light(插头dp、位运算)
比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...
- 插头DP专题
建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...
- HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)
插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...
- HDU 1693 Eat the Trees(插头DP)
题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...
- HDU 4064 Carcassonne(插头DP)(The 36th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4064 Problem Description Carcassonne is a tile-based ...
- URAL 1519 基础插头DP
题目大意: 给定一个图,一部分点'*'作为障碍物,求经过所有非障碍点的汉密尔顿回路有多少条 基础的插头DP题目,对于陈丹琦的论文来说我觉得http://blog.sina.com.cn/s/blog_ ...
- uva 11270 - Tiling Dominoes(插头dp)
题目链接:uva 11270 - Tiling Dominoes 题目大意:用1∗2木块将给出的n∗m大小的矩阵填满的方法总数. 解题思路:插头dp的裸题,dp[i][s]表示第i块位置.而且该位置相 ...
随机推荐
- PHP获取当前毫秒级别时间戳
PHP提供了一个microtime()函数,调用时不带可选参数,本函数以"msec sec"的格式返回一个字符串,其中 sec 是自 Unix 纪元(0:00:00 January ...
- C++ Primer第5版 第一章课后练习
练习1.9 #include <iostream> int main() { int sum = 0, val = 50; while (val <= 100) { sum += v ...
- UEditor 自定义图片视频尺寸校验
UEditor支持单图.多图以及视频上传,编辑器配置项支持文件格式.文件大小校验,对于文件宽高尺寸校验暂不支持.这里记录一下自定义图片.视频尺寸校验过程,内容核心主要是扩展校验逻辑和增加自定义提示文本 ...
- HTML 5标签中<button>的新属性
<button> 标签HTML5 中的新属性 属性 值 描述 autofocus autofocus 规定当页面加载时按钮应当自动地获得焦点. disabled disabled 规定应该 ...
- servlet 验证生命周期过程调用方法的次数
1.书写一个servlet并编译,如: package testservlet; import java.io.IOException;import java.io.PrintWriter; impo ...
- 走在深夜的小码农 Seventh Day
Css3 Seventh Day writer:late at night codepeasant 学习大纲: 1. 定位(position) 介绍 1.1 为什么使用定位 我们先来看一个效果,同时思 ...
- 前端未来趋势之原生API:Web Components
声明:未经允许,不得转载. Web Components 现世很久了,所以你可能听说过,甚至学习过,非常了解了.但是没关系,可以再重温一下,温故知新. 浏览器原生能力越来越强. js 曾经的 JQue ...
- 老师问学生while判断
老师问学生,这道题你会做了吗?如果学生答"会了(y)",则可以放学.如果学生不会做(n),则老师再讲一遍,再问学生是否会做了...... (1)直到学生会为止,才可以放学. (2) ...
- 10 XSRF和XSS
10 XSRF和XSS CSRF(Cross-site request forgery)跨站请求伪造 XSS(Cross Site Scripting)跨站脚本攻击 CSRF重点在请求,XSS重点在脚 ...
- 分布式文档存储数据库之MongoDB分片集群
前文我们聊到了mongodb的副本集以及配置副本集,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13953598.html:今天我们来聊下mongodb的分片 ...