好学易懂 从零开始的插头DP(一)
好学易懂 从零开始的插头DP(一)
写在前面
这是一篇,以蒟蒻视角展开的梳理总结。更改了一些顺序,变化了一些细节。方便蒟蒻学习理解(起码本蒟蒻是这样)。大佬们可以直接看其它大佬的博客,可以学的更快。
你必须要学会的前置知识:状态压缩DP
学不会依旧可以读,但是推荐学的前置知识:哈希
论文贡前面,建议读完博客再看。
《基于连通性状态压缩的动态规划问题》
什么是插头DP
很显然,是一个关于插头的动态规划。那么,什么是插头呢?
如图我们在一个方格内,关于格点画一条闭合回路。
对于每一个方格,内部,有六种情况
不难发现,对于回路里的任何一个方格,四条边中,有且仅有两个与表示路径的蓝色线相交。这也很好理解,进一次,出一次,C(4,2)=6。
我们现在把格子里的蓝色线条,变成从格子中心指向外边的→。
这个箭头,也就是所谓的插头。
例题
我们结合一个例题来看,这个题目是洛谷模板题的弱化版,很多博客放在了模板题后的第一题,结合个人经历我觉得它比模板题更适合作第一题。
题目链接:HDU1693 or 洛谷P5074
题目大意:给出n*m的方格,有些格子不能铺线,其它格子必须铺,可以形成多个闭合回路。问有多少种铺法?(1<=n,m<=12)
那么,把回路模型变成插头模型有什么好处或者性质呢?
1:首先,我们可以发现,如果一个格子上方的格子有下插头,那这个格子一定有上插头。其它方向类似。
2:按1的方法设置插头,最后一定会构成回路。
3:一个格子的合理取法合且仅合相邻的格子有关。
观察下第三点,它其实代表了无后效性。假设我们从上到下,从左到右的处理每一个格子,那么我们只需要记录部分格子的状态即可,再往上的格子具体状态不用知道。
如上图,对于当前格子,我们只需要知道红色的这些格子就行了,再上面的格子具体的取法,已经不会对下面任何未处理的格子产生影响。
已经掌握了状态压缩的你,一定能轻松的算出状态总数,每个格子6种,维护n个格子。总共6n6^n6n种状态,好的,完蛋,只有2e9个状态。
别急,我们真的需要2e9个状态嘛?这些格子里,指向彼此和已经处理过的格子的插头,显然是废物信息。我们实际上只需要知道这些插头嘛:
蓝色的是其它格子需要用到的,黄色的是当前格子需要用到的。我们叫红色的这个线为轮廓线。我们只需要知道这轮廓线上m+1个箭头是否存在就可以了。总共2m∗22^m*22m∗2个状态。再乘上n和m,时空复杂度都绰绰有余。
那么,怎么实现呢?我们要解决两个问题。
1:已知这些插头的情况下,这个方格该如何填。
2:填完这个方格后,如何得到下一个方格所需要的插头状态,更特殊的,如何从上一行行末,变到下一行行初。
这两个问题,其实都不是很难,稍微思考下,都可以独立解答,建议思考后再往下看。图片挡下文大法。
问题1:
0:若这个格子不能走,则不能存在左侧和上方插头。
1:如果当前格子存在左侧插头和上方插头,那么只有一种合理填法。
2:如果仅存在左侧插头,那么有两种合理填法。
3:如果仅存在上方插头,那和上一种类似,也是两种填法。
4:如果都不存在呢?只有一种填法
问题2:
解答了问题1,显然我们也得到了问题2的解答,毕竟我们填出了这个格子,自然知道插头分布。唯一特殊的是上一行末到这一行头的处理。上一行末不可能有右插头,那我们直接把上一行末状态的表示最后是否存在右插头的位置去掉,再添加一个表示没有左插头的位,不就表示出了这一行第一个的状态了嘛,为了方便写,下方的代码里,我用dp[i][0][mask]表示转移后的上一行行末状态。
到这里,我们已经得到了解法了,成熟的评测机,应该自动AC了吧(划去)。插头DP还是要多写的,千万自己写一遍,别忘了,这只是模板题的弱化。
这里提供一份代码(洛谷AC)
#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std;
int n,m,maxk,a[13][13];
long long dp[13][13][1<<14];
void init()
{ scanf("%d%d",&n,&m);maxk=(1<<(m+1))-1;for (int i=1;i<=n;i++){for (int j=1;j<=m;j++){scanf("%d",&a[i][j]);}}memset(dp,0,sizeof(dp));
}
void solve()
{int prei,prej;dp[0][m][0]=1;for (int i=1;i<=n;i++){for (int k=0;k<=maxk;k++){dp[i][0][k<<1]=dp[i-1][m][k];}for (int j=1;j<=m;j++){prei=i;prej=j-1;for (int k=0;k<=maxk;k++){int b1=(k>>(j-1))&1;int b2=(k>>j)&1;if (!a[i][j]){if (!b1&&!b2) dp[i][j][k]+=dp[prei][prej][k];}else if (!b1&&!b2){dp[i][j][k+(1<<j)+(1<<(j-1))]+=dp[prei][prej][k];}else if (b1&&!b2){dp[i][j][k]+=dp[prei][prej][k];dp[i][j][k+(1<<(j-1))]+=dp[prei][prej][k];}else if (!b1&&b2){dp[i][j][k]+=dp[prei][prej][k];dp[i][j][k-(1<<(j-1))]+=dp[prei][prej][k];}else if (b1&&b2){dp[i][j][k-(1<<j)-(1<<(j-1))]+=dp[prei][prej][k];}}}}printf("%lld\n",dp[n][m][0]);
}
int main()
{int t;scanf("%d",&t);while (t--){init();solve();}return 0;
}
到这里,插头DP算是入门了一半了,下一篇博客,将会介绍模板题和一系列的简单应用。这道题目里的状态是二进制状态压缩,下面的题目则不然,敬请期待。想必,多半,大概,也许,可能,能日更吧。
电脑前这个努力的帅气身影是谁呢?そう 私 です
好学易懂 从零开始的插头DP(一)相关推荐
- 好学易懂 从零开始的插头DP(三)
好学易懂 从零开始的插头DP(三) 写在前面 这篇文章主要是介绍一些括号表示法和简单回路的基本变化,下一篇会是一些非回路(最小表示),毒瘤状态(正wa着所以咕着),结合矩阵乘法加速等一些复杂应用.下下 ...
- bzoj1814 Ural 1519 Formula 1(插头dp模板题)
1814: Ural 1519 Formula 1 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 924 Solved: 351 [Submit][S ...
- [入门向选讲] 插头DP:从零概念到入门 (例题:HDU1693 COGS1283 BZOJ2310 BZOJ2331)
转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/7326874.html 最近搞了一下插头DP的基础知识--这真的是一种很锻炼人的题型-- 每一道题的状态都不一样 ...
- HDU4084 插头dp
题意:给定一个图,0是不能放的,然后现在有1X1和1X2方块,最后铺满该图,使得1X1使用次数在C到D之间,1X2次数随便,问有几种放法 思路:插头DP或轮廓线,多加一维DP讨论就可以 注意插头DP状 ...
- POJ3133(插头dp)
传送门:http://poj.org/problem?id=3133 Manhattan Wiring Time Limit: 5000MS Memory Limit: 65536K ...
- P3272 [SCOI2011]地板(插头DP)
[题面链接] https://www.luogu.org/problemnew/show/P3272 [题目描述] 有一个矩阵,有些点必须放,有些点不能放,用一些L型的图形放满,求方案数 [题解] ( ...
- POJ 3133 Manhattan Wiring (插头DP)
Manhattan Wiring Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 1110 Accepted: 634 D ...
- URAL1519 Formula 1 —— 插头DP
题目链接:https://vjudge.net/problem/URAL-1519 1519. Formula 1 Time limit: 1.0 second Memory limit: 64 MB ...
- [集训队作业2018]小Z的礼物(min-max容斥,插头dp)
传送门 这种求 "取到所有物品的期望时间" 的题一般都用 min−maxmin-maxmin−max容斥 解决: 设t(i,j)t(i,j)t(i,j)为取到格子(i,j)(i,j ...
- 【BZOJ1814】Ural 1519 Formula 1 插头DP
[BZOJ1814]Ural 1519 Formula 1 题意:一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数.(n,m<=12) 题解:插头DP板子题,刷板 ...
最新文章
- 如何获取公众号中html,[技巧] 如何获取公众号图文里的视频地址
- HDU1556(树状数组模版)
- Java 中的 String 真的是不可变的吗
- dsp实现快速傅里叶的C语言程序,DSP-快速傅立叶变换(FFT)算法实验
- 区块链技术学习之-简单加密货币-高飞币
- SWT、JavaFx十种页面布局快速理解
- Python 回归 普通最小二乘法(Ordinary Least Squares)
- 减少php,如何减少PHP代码?
- 线下产品风控门道真不少
- rpm 安装、卸载软件命令 ——以nginx为例
- 二、图的遍历——深度优先遍历
- ubuntu18.04 桌面不定时crash重置问题
- JavaWEB作业 用CSS制作高仿QQ空间
- RxJava模式与原理-----标准观察者与RxJava观察者
- Android 设备兼容-[Android_YangKe]
- 深入浅出实时数据库12.8日版
- 分布式事务2PC论文翻译(Concurrency Control and Recovery in Database Systems )
- 转盘and狼人杀源码
- 盛世五洲flash进入页 动画设计制作
- 战队口号霸气押韵8字_三个字的公司名字怎么起?
热门文章
- Ubuntu20.04安装mujoco
- Geek(一个好用的强力卸载软件工具,包括注册表所有依赖项全部清理掉)
- mysql建考勤信息代码_基于Java+JSP+Servlet+Mysql的考勤管理系统
- 微型计算机原理实验二,微型计算机原理与接口技术实验指导(第2版)
- c语言程序设计会出现什么问题,计算机C语言程序设计过程中的常见问题分析和研究...
- 计算机网络基础知识整理
- Google翻译插件不能用解决方案
- 设置全局键盘钩子和消息钩子
- 计算机共享w7系统文件共享,win7系统怎么共享文件 电脑一键共享文件方法教程...
- GAN与自动编码器:深度生成模型的比较