想当初,我听见大佬们谈起插头DP时,觉得插头DP是个神仙的东西。

某大佬:“考场见到插头DP,直接弃疗。

现在,我终于懂了他们为什么这么说了。

因为——

插头DP很毒瘤!

为什么?

等等你就知道这是为什么了。


例题

hdu 1693 Eat the Trees

题目大意:给你一个地图,每个格子是空地或者障碍。现在要用若干个回路来覆盖这些空地,使得每个空地皆被一个回路覆盖。问方案数。

n,m<=11

How to solve it?

暴力?本人的暴力水平太弱了,对于这题,似乎连暴力都不好打。

So?咋做?

Of course,DP……

怎么划分阶段?

通常有逐行,逐列,逐格。你说的是什么鬼?

字面上的意思,逐行就是从上一行的状态转移到下一行,逐列类似。

逐格就是按照一定的顺序枚举每个格子(比如从上到下,从左到右),从上一个格子转移到下一个格子。

插头DP

插头是什么?

插头DP,最重要的当然是插头。

对于每一个格子,有上下左右四个插头,表示这个格子可以在这个方向与外面相连。

这题中,每个点的插头状况有下面七种。



其中,第0种情况是存在于障碍物的,剩下的6种存在于空地。

那么,我们在转移时,要接上轮廓线上边的插头,使其不会出现断头的情况。

轮廓线?就是你转移时候的一条线(没说一定是直线哈),这条线上面的每个格子都是你之前计算过的。



这幅图就是逐行转移的轮廓线。

做法

考虑逐行转移。

设fi,s" role="presentation">fi,sfi,s表示枚举到第i" role="presentation">ii行,轮廓线上的下插头状态为s" role="presentation">ss的方案数。

怎么转移?

既然这是插头DP,那么转移的时候,就要接上上面的插头。

仔细地考虑一下,转移状态太多,不好枚举,时间复杂度显然无法接受。

那我们就试一下逐格转移。

先放个图。



设fi,j,s" role="presentation">fi,j,sfi,j,s表示,枚举到i" role="presentation">ii行j" role="presentation">jj列,轮廓线上的插头状态为s" role="presentation">ss的方案数。

轮廓线上的插头显然是m" role="presentation">mm个下插头和1个右插头。

那么,转移?

既然要接上之前的插头,那么我们可以让之前的插头来接上它。

比如,1状态的格子的上方要有一个下插头,左边要有一个右插头。

2状态的格子上方要有一个下插头,左边不能有右插头。

那么我们枚举现在的状态,可以算出它从之前的那个状态推过来。

这个需要分类讨论……这就是插头DP毒瘤的原因。

还有一个比较毒瘤的是,最上面一行,最下面一行,以及最左边一列,需要特殊处理。

当然,如果你喜欢,最上面一行也不需特殊处理。

为什么要特殊处理?我觉得我不用说原因了。

代码

下面的代码,注释中例如no*yes,表示上面没有下插头,左边有一个右插头。

话说存状态的话,听说处理连通性的插头DP要按顺序存,这里为图方便,就将右插头存到了最后。

  1. /*Situation:
  2. 1:up left
  3. 2:down up
  4. 3:down left
  5. 4:down right
  6. 5:right left
  7. 6:right up
  8. */
  9. using namespace std;
  10. #include <cstdio>
  11. #include <cstring>
  12. #include <algorithm>
  13. int n,m;
  14. int mat[11][11];
  15. long long f[11][11][4096];
  16. int main()
  17. {
  18. // freopen("in.txt","r",stdin);
  19. // freopen("test.txt","w",stdout);
  20. int T;
  21. scanf("%d",&T);
  22. for (int TT=1;TT<=T;++TT)
  23. {
  24. scanf("%d%d",&n,&m);
  25. for (int i=0;i<n;++i)
  26. for (int j=0;j<m;++j)
  27. scanf("%d",&mat[i][j]);
  28. memset(f,0,sizeof f);
  29. if (mat[0][0])
  30. f[0][0][1<<0|1<<m]=1;//4
  31. else
  32. f[0][0][0]=1;//Space
  33. for (int j=1;j<m;++j)
  34. if (mat[0][j])
  35. {
  36. for (int s_=0;s_<1<<j+1;++s_)
  37. if (s_>>j&1)
  38. {
  39. int s=s_|1<<m;
  40. f[0][j][s]+=f[0][j-1][s_^1<<j];//no*no->4
  41. s=s_;
  42. f[0][j][s]+=f[0][j-1][s_^1<<j|1<<m];//no*yes->3
  43. }
  44. else
  45. {
  46. int s=s_|1<<m;
  47. f[0][j][s]+=f[0][j-1][s_|1<<m];//no*yes->5
  48. }
  49. }
  50. else
  51. {
  52. for (int s=0;s<1<<j;++s)
  53. f[0][j][s]+=f[0][j-1][s];
  54. }
  55. for (int i=1;i<n-1;++i)
  56. {
  57. if (mat[i][0])
  58. {
  59. for (int s_=0;s_<(1<<m);++s_)
  60. if (s_&1)
  61. {
  62. int s=s_|1<<m;
  63. f[i][0][s]+=f[i-1][m-1][s_^1];//no*no->4
  64. s=s_;
  65. f[i][0][s]+=f[i-1][m-1][s_];//yes*no->2
  66. }
  67. else
  68. {
  69. int s=s_|1<<m;
  70. f[i][0][s]+=f[i-1][m-1][s_|1];//yes*no->6
  71. }
  72. }
  73. else
  74. {
  75. for (int s=0;s<(1<<m);s+=2)
  76. f[i][0][s]+=f[i-1][m-1][s];//no*no->Space
  77. }
  78. for (int j=1;j<m;++j)
  79. if (mat[i][j])
  80. {
  81. for (int s_=0;s_<1<<m;++s_)
  82. if (s_>>j&1)
  83. {
  84. int s=s_|1<<m;
  85. f[i][j][s]+=f[i][j-1][s_^1<<j];//no*no->4
  86. s=s_;
  87. f[i][j][s]+=f[i][j-1][s_]+f[i][j-1][s_^1<<j|1<<m];//yes*no->2 no*yes->3
  88. }
  89. else
  90. {
  91. int s=s_|1<<m;
  92. f[i][j][s]+=f[i][j-1][s_|1<<m]+f[i][j-1][s_|1<<j];//no*yes->5 yes*no->6
  93. s=s_;
  94. f[i][j][s]+=f[i][j-1][s_|1<<j|1<<m];//yes*yes->1
  95. }
  96. }
  97. else
  98. {
  99. for (int s=0;s<1<<m;++s)
  100. if (!(s>>j&1))
  101. f[i][j][s]+=f[i][j-1][s];//no*no->Space
  102. }
  103. }
  104. if (mat[n-1][0])
  105. {
  106. for (int s_=0;s_<(1<<m);++s_)
  107. if (!(s_&1))
  108. {
  109. int s=s_|1<<m;
  110. f[n-1][0][s]+=f[n-1-1][m-1][s_|1];//yes*no->6
  111. }
  112. }
  113. else
  114. {
  115. for (int s=0;s<(1<<m);s+=2)
  116. f[n-1][0][s]+=f[n-1-1][m-1][s];//no*no->Space
  117. }
  118. for (int j=1;j<m;++j)
  119. if (mat[n-1][j])
  120. {
  121. for (int s_=0;s_<1<<m;++s_)
  122. if (!(s_>>j&1))
  123. {
  124. int s=s_|1<<m;
  125. f[n-1][j][s]+=f[n-1][j-1][s_|1<<m]+f[n-1][j-1][s_|1<<j];//no*yes->5 yes*no->6
  126. s=s_;
  127. f[n-1][j][s]+=f[n-1][j-1][s_|1<<j|1<<m];//yes*yes->1
  128. }
  129. }
  130. else
  131. {
  132. for (int s=0;s<1<<m;++s)
  133. if (!(s>>j&1))
  134. f[n-1][j][s]+=f[n-1][j-1][s];//no*no->Space
  135. }
  136. long long ans=f[n-1][m-1][0];
  137. printf("Case %d: There are %lld ways to eat the trees.\n",TT,ans);
  138. }
  139. return 0;
  140. }

总结

插头DP的关键是,转移的时候要连上之前的插头,保证不要断掉。

打插头DP需要非常严谨,打错了就不妙了,还不好调。

某大佬说,这不是标准的插头DP,因为标准的插头DP还要维护连通性,好像是用什么括号序。

我才懒得管这么多,感性理解一下,插头DP就是有插头的DP吧……不过那些维护连通性的题,若有机会,我定会光顾一下。

这题作为例题,还是很好的。

hdu1693 Eat the Trees [插头DP经典例题]的更多相关文章

  1. HDU1693 Eat the Trees —— 插头DP

    题目链接:https://vjudge.net/problem/HDU-1693 Eat the Trees Time Limit: 4000/2000 MS (Java/Others)    Mem ...

  2. HDU1693 Eat the Trees 插头dp

    原文链接http://www.cnblogs.com/zhouzhendong/p/8433484.html 题目传送门 - HDU1693 题意概括 多回路经过所有格子的方案数. 做法 最基础的插头 ...

  3. HDU 1693 Eat the Trees(插头DP)

    题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...

  4. hdu 1693 Eat the Trees——插头DP

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1693 第一道插头 DP ! 直接用二进制数表示状态即可. #include<cstdio> # ...

  5. HDU 1693 Eat the Trees ——插头DP

    [题目分析] 吃树. 直接插头DP,算是一道真正的入门题目. 0/1表示有没有插头 [代码] #include <cstdio> #include <cstring> #inc ...

  6. hdu1693:eat trees(插头dp)

    题目大意: 题目背景竟然是dota!屠夫打到大后期就没用了,,只能去吃树! 给一个n*m的地图,有些格子是不可到达的,要把所有可到达的格子的树都吃完,并且要走回路,求方案数 题解: 这题大概是最简单的 ...

  7. hdu1693 Eat the Trees 【插头dp】

    题目链接 hdu1693 题解 插头\(dp\) 特点:范围小,网格图,连通性 轮廓线:已决策点和未决策点的分界线 插头:存在于网格之间,表示着网格建的信息,此题中表示两个网格间是否连边 状态表示:当 ...

  8. [Hdu1693]Eat the Trees(插头DP)

    Description 题意:在n*m(1<=N, M<=11 )的矩阵中,有些格子有树,没有树的格子不能到达,找一条或多条回路,吃完所有的树,求有多少种方法. Solution 插头DP ...

  9. 2019.01.23 hdu1693 Eat the Trees(轮廓线dp)

    传送门 题意简述:给一个有障碍的网格图,问用若干个不相交的回路覆盖所有非障碍格子的方案数. 思路:轮廓线dpdpdp的模板题. 同样是讨论插头的情况,只不过没有前一道题复杂,不懂的看代码吧. 代码: ...

随机推荐

  1. C++ BASS 实例

    #include <iostream> #include <string> #include <map> #include "..\sdk\bass\in ...

  2. spring Cache + Redis 开发数据字典以及自定义标签

    一.数据库表结构 1.  分类表:dict_type 2.  子项表:dict_entry 二.页面维护功能示意图: 1.  分类管理 点击子项管理进入子项管理页面 2.子项管理 三.数据字典添加到缓 ...

  3. tornado接收ajax的post请求报错WARNING:tornado.access:405 OPTIONS /add

    后端报错信息 WARNING:tornado.access:405 OPTIONS /add (::1) 1.00m 前端报错信息 2xhr.js?ec6c:172 OPTIONS http://lo ...

  4. CentOS 7.2部署NTP服务器实现时间同步

    CentOS 7.2部署NTP服务器实现时间同步 [日期:2017-12-18] 来源:Linux社区  作者:梁明远 [字体:大 中 小]   1. 前言 对于容器编排系统,前段时间主要研究kube ...

  5. 手工编写JavaWeb项目

    手工编写JavaWeb项目 一.打开Tomcat服务器 二.编写简单的web项目 三.访问项目 并且,tomcat服务器也是可以直接访问.txt的,其实就和其它的web服务器一样,什么都可以访问,和之 ...

  6. Activity详解三 启动activity并返回结果 转载 https://www.cnblogs.com/androidWuYou/p/5886991.html

    首先看演示: 1 简介 .如果想在Activity中得到新打开Activity 关闭后返回的数据,需要使用系统提供的startActivityForResult(Intent intent, int ...

  7. Django rest framework (视图类详解)

    官网:https://www.django-rest-framework.org/api-guide/viewsets/ 在django rest framework 视图中一共有N个类 第一类:AP ...

  8. openstack各组件介绍

    Nova:计算服务,通过虚拟化技术,实现虚拟机的创建,管理,删除,是openstack中最核心的服务. Neutron:网络服务,为虚拟机提供网络连接服务,就像物理机的交换机和路由器一样 Glance ...

  9. Git初次使用,记录自己看

    Git官网下载:https://git-scm.com/downloads 官网如果太慢,可以去这下载:http://www.wmzhe.com/soft-38801.html,注意选择如下图地址下载 ...

  10. AngularJs 报错 Error: [$parse:lexerr]

    参考:https://www.cnblogs.com/fangshidaima/p/6048071.html 错误: 根据错误找到报错行: $scope.$apply($scope.param1 = ...