【题目大意】

给出一个m*n的矩阵里面有一些格子为障碍物,求经过所有非障碍格子的哈密顿回路个数。

【思路】

最典型的插头DP。分为三种情况:

(1)当前格子既没有上插头也没有左插头。

如果下边和右边都没有障碍,新建连同分量。

(2)如果只有左插头或者右插头。

延伸或者拐弯,当然也要判断有没有障碍。

(3)上插头和左插头都没有。

1. 如果两个插头不连通(编号不一样),那么将两个插头所处的连通分量合并,标记相同的连通块标号,O(n)扫描保证最小表示;
2. 如果已经连通,相当于出现了一个回路,这种情况只能出现在最后一个非障碍格子。

由于状态非常多,用hash表存储状态。

decode和encode注意一下,这里不赘述了。

【错误点】

注意一下ch要开得够大,具体见代码。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<vector>
  6. using namespace std;
  7. typedef long long ll;
  8. const int MAXN=;
  9. const int HASH=;
  10. int ex,ey;
  11. int m,n;
  12. int maze[MAXN][MAXN];
  13. int code[MAXN],ch[MAXN];
  14. struct HashMap
  15. {
  16. vector<int> hash[HASH];//存储f和state的下标
  17. vector<ll> f,state;//存储对应的方案数和状态
  18. void init()
  19. {
  20. for (int i=;i<HASH;i++) vector<int>().swap(hash[i]);
  21. vector<ll>().swap(f);
  22. vector<ll>().swap(state);
  23. }
  24. void push(ll st,ll ans)
  25. {
  26. int h=st%HASH;
  27. for (int i=;i<hash[h].size();i++)
  28. {
  29. int now=hash[h][i];
  30. if (state[now]==st)//如果已经存储了当前状态,直接累加
  31. {
  32. f[now]+=ans;
  33. return;
  34. }
  35. }
  36. //如果没有存储过当前状态,累加
  37. state.push_back(st);
  38. f.push_back(ans);
  39. hash[h].push_back(state.size()-);
  40. }
  41. }dp[];
  42.  
  43. void decode(ll st)
  44. {
  45. memset(code,,sizeof(code));
  46. for (int i=n;i>=;i--)
  47. {
  48. code[i]=st&;//每三位代表一个信息
  49. st>>=;
  50. }
  51. }
  52.  
  53. ll encode()
  54. //用最小表示法重新编码
  55. {
  56. int cnt=;
  57. memset(ch,-,sizeof(ch));
  58. ch[]=;
  59. long long st=;
  60. for (int i=;i<=n;i++)
  61. {
  62. if (ch[code[i]]==-) ch[code[i]]=cnt++;
  63. code[i]=ch[code[i]];
  64. st<<=;
  65. st|=code[i];
  66. }
  67. return st;
  68. }
  69.  
  70. void shift()
  71. {
  72. for (int i=n;i>;i--) code[i]=code[i-];
  73. code[]=;
  74. }
  75.  
  76. void dpblank(int i,int j,int cur)
  77. {
  78. for (int k=;k<dp[-cur].state.size();k++)
  79. {
  80. decode(dp[-cur].state[k]);
  81. int left=code[j-];//左插头
  82. int up=code[j];//上插头
  83.  
  84. /*如果上下插头都没有*/
  85. if (!left && !up)
  86. {
  87. if (maze[i][j+] && maze[i+][j])
  88. {
  89. code[j-]=code[j]=MAXN-;
  90. //这里只要随便设置一个大数即可
  91.  
  92. //【attention】这里千万不可以设置成MAXN,否则ch数组会抱★★★★★★★★
  93.  
  94. //因为encode会重新用最小表示法编码
  95. dp[cur].push(encode(),dp[-cur].f[k]);
  96. }
  97. }
  98.  
  99. /*只有上插头或者只有左插头*/
  100. if ((left&&(!up))||((!left)&&up))
  101. {
  102.  
  103. int t=left|up;
  104. if (maze[i][j+])//右边没有障碍
  105. {
  106. code[j-]=;
  107. code[j]=t;
  108. dp[cur].push(encode(),dp[-cur].f[k]);
  109. }
  110. if (maze[i+][j])//下面没有障碍
  111. {
  112. code[j-]=t;
  113. code[j]=;
  114. if (j==n) shift();
  115. dp[cur].push(encode(),dp[-cur].f[k]);
  116. }
  117. }
  118.  
  119. /*上插头和右插头都有*/
  120. if (left && up)
  121. {
  122. if (left==up)
  123. {
  124. if (i==ex && j==ey)
  125. {
  126. code[j-]=code[j]=;
  127. if (j==n) shift();
  128. dp[cur].push(encode(),dp[-cur].f[k]);
  129. }
  130. }
  131. else
  132. {
  133. code[j-]=code[j]=;
  134. for (int t=;t<=n;t++)
  135. if (code[t]==up) code[t]=left;
  136. if (j==n) shift();
  137. dp[cur].push(encode(),dp[-cur].f[k]);
  138. }
  139. }
  140. }
  141. }
  142.  
  143. void dpblock(int i,int j,int cur)
  144. {
  145. int k=;
  146. for (int k=;k<dp[-cur].state.size();k++)
  147. {
  148. decode(dp[-cur].state[k]);
  149. code[j-]=code[j]=;
  150. if (j==n) shift();
  151. dp[cur].push(encode(),dp[-cur].f[k]);
  152. }
  153. }
  154.  
  155. void solve()
  156. {
  157. int cur=;
  158. ll ans=;
  159. dp[cur].init();
  160. dp[cur].push(,);//DP数组初始化
  161. for (int i=;i<=m;i++)
  162. for (int j=;j<=n;j++)
  163. {
  164. cur^=;
  165. dp[cur].init();
  166. if (maze[i][j]) dpblank(i,j,cur);
  167. else dpblock(i,j,cur); }
  168. for (int i=;i<dp[cur].state.size();i++)
  169. ans+=dp[cur].f[i];
  170. printf("%lld",ans);
  171. }
  172.  
  173. void init()
  174. {
  175. memset(maze,,sizeof(maze));
  176. ex=ey=;
  177. for (int i=;i<=m;i++)
  178. {
  179. char str[MAXN];
  180. scanf("%s",str);
  181. for (int j=;j<n;j++)
  182. {
  183. if (str[j]=='.')
  184. {
  185. ex=i;
  186. ey=j+;
  187. maze[i][j+]=;
  188. }
  189. }
  190. }
  191. }
  192.  
  193. int main()
  194. {
  195. while (scanf("%d%d",&m,&n)!=EOF)
  196. {
  197. init();
  198. if (ex==) puts("");//如果没有一个是空格的话直接输出0
  199. else solve();
  200. }
  201. return ;
  202. }

【插头DP】BZOJ1814-Formula的更多相关文章

  1. 【BZOJ1814】Ural 1519 Formula 1 (插头dp)

    [BZOJ1814]Ural 1519 Formula 1 (插头dp) 题面 BZOJ Vjudge 题解 戳这里 上面那个链接里面写的非常好啦. 然后说几个点吧. 首先是关于为什么只需要考虑三进制 ...

  2. 【BZOJ1814】Ural 1519 Formula 1 插头DP

    [BZOJ1814]Ural 1519 Formula 1 题意:一个 m * n 的棋盘,有的格子存在障碍,求经过所有非障碍格子的哈密顿回路个数.(n,m<=12) 题解:插头DP板子题,刷板 ...

  3. bzoj1814 Ural 1519 Formula 1(插头dp模板题)

    1814: Ural 1519 Formula 1 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 924  Solved: 351[Submit][Sta ...

  4. bzoj1814 Ural 1519 Formula 1(插头DP)

    对插头DP的理解还不是很透彻. 先说一下肤浅的理解吧. 插头DP使用范围:指数级复杂度,且适用于解决网格图连通性问题,如哈密顿回路等问题.插头一般指每相邻2个网格的接口. 题目难度:一般不可做. 使用 ...

  5. 插头DP讲解+[BZOJ1814]:Ural 1519 Formula 1(插头DP)

    1.什么是插头$DP$? 插头$DP$是$CDQ$大佬在$2008$年的论文中提出的,是基于状压$D$P的一种更高级的$DP$多用于处理联通问题(路径问题,简单回路问题,多回路问题,广义回路问题,生成 ...

  6. 【Ural】1519. Formula 1 插头DP

    [题目]1519. Formula 1 [题意]给定n*m个方格图,有一些障碍格,求非障碍格的哈密顿回路数量.n,m<=12. [算法]插头DP [题解]<基于连通性状态压缩的动态规划问题 ...

  7. RUAL1519 Formula 1 【插头DP】

    RUAL1519 Formula 1 Background Regardless of the fact, that Vologda could not get rights to hold the ...

  8. URAL 1519 Formula 1(插头DP,入门题)

    Description Background Regardless of the fact, that Vologda could not get rights to hold the Winter ...

  9. URAL1519 Formula 1 —— 插头DP

    题目链接:https://vjudge.net/problem/URAL-1519 1519. Formula 1 Time limit: 1.0 secondMemory limit: 64 MB ...

  10. ural 1519 Formula 1(插头dp)

    1519. Formula 1 @ Timus Online Judge 干了一天啊!!!插头DP入门. 代码如下: #include <cstdio> #include <cstr ...

随机推荐

  1. 面向对象 ( OO ) 的程序设计——继承

    本文地址:http://www.cnblogs.com/veinyin/p/7608282.html  仅支持实现继承,且主要依靠原型链来实现,不过一般会混合构造函数一起实现继承 1 原型链 继承使用 ...

  2. Yii2 的 redis 应用

    在应用的时候需要先对yii2进行扩展安装 如果装有composer直接运行 php composer.phar require --prefer-dist yiisoft/yii2-redis 当然也 ...

  3. [AHOI2012]树屋阶梯 题解(卡特兰数)

    [AHOI2012]树屋阶梯 Description 暑假期间,小龙报名了一个模拟野外生存作战训练班来锻炼体魄,训练的第一个晚上,教官就给他们出了个难题.由于地上露营湿气重,必须选择在高处的树屋露营. ...

  4. 【leetcode 简单】第二十七题 二叉树的最小深度

    给定一个二叉树,找出其最小深度. 最小深度是从根节点到最近叶子节点的最短路径上的节点数量. 说明: 叶子节点是指没有子节点的节点. 示例: 给定二叉树 [3,9,20,null,null,15,7], ...

  5. redis中插入用户集合的语句,有四个属性

    一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 ...

  6. CentOS7.4 安装 oracle12c

    安装依赖 yum install -y binutils.x86_64 compat-libcap1.x86_64 gcc.x86_64 gcc-c++.x86_64 glibc.i686 glibc ...

  7. LFM隐语义模型Latent Factor Model

    实际应用 LFM 模型在实际使用中有一个困难,就是很难实现实时推荐.经典的 LFM 模型每次训练都需要扫描所有的用户行为记录,并且需要在用户行为记录上反复迭代来优化参数,所以每次训练都很耗时,实际应用 ...

  8. linux下rz,sz安装

    1.sz rz yum安装 yum install lrzsz

  9. 关于VS2010的一些操作

    自动插入接口实现 1: class MyClass : IMyInterface 2: { 3:   4: } .csharpcode, .csharpcode pre { font-size: sm ...

  10. spring mvc整合mybaitis和log4j

    在上一篇博客中,我介绍了在mac os上用idea搭建spring mvc的maven工程,但是一个完整的项目肯定需要数据库和日志管理,下面我就介绍下spring mvc整合mybatis和log4j ...