给你一个 \(n \times m\) 的棋盘,有的格子是障碍,问共有多少条回路满足经过每个非障碍格子恰好一次。
如图,\(n=m=4\),\((1,1),(1,2)\) 是障碍,共有 \(2\) 条满足要求的回路。
输入格式
第一行包含两个整数 \(n,m\)。
接下来 \(n\) 行,每行包含一个长度为 \(m\) 的字符串,字符串中只包含 *
和 .
,其中 *
表示障碍格子,.
表示非障碍格子。
输出格式
输出一个整数,表示满足条件的回路数量。
数据范围
\(2 \le n,m \le 12\)
输入样例:
4 4
**..
....
....
....
输出样例:
2
解题思路
插头dp
插头dp 主要用来解决一些网格图上的哈密顿回路方案的相关问题
本题即 插头dp 模板题,即给出一个网格图,并且要求一些格子不能经过,求哈密顿回路的方案数
做法通常是一格一格做,由于是哈密顿回路,即每个格子会有两条出边(即插头),处理当前格子的时候之前,记录上一行边界出边的状态,通常记录这些的状态有两种方法:
- 最小表示法:由于上面每一个出边,一定会对应另外一条出边,对于每一对匹配边,用同一个数字表示,不同对匹配边之间用不同的数字表示,最后所有的这样的数字连起来的最小表示法即为该边界的状态
- 括号表示法(效率一般更高,因为可以转化为进制之间的运算):从左往右,如果没有当前边界没有出边则用 \(0\) 表示,否则如果该匹配边在左边用 \(1\) 表示,在右边用 \(2\) 表示,最后会形成 \(012\) 这样的数字,但是这样的数字得用四进制表示,原因在于四进制好进行位运算
状态表示:\(f[i][j][s]\) 表示处理到 \((i,j)\) 这个格子时边界状态为 \(s\) 时得方案数
状态计算:
插头dp 的状态转移一般比较复杂,需要分好多情况讨论(这里采用括号表示法):
处理到 \((i,j)\) 这个格子的时候,其左边界插头的状态为 \(x\),上边界插头的状态为 \(y\)(\(0\leq x,y\leq 2\))
- 如果 \((i,j)\) 是障碍物,则要求 \((i,j)\) 上不能出现插头才是合法状态,即 \(x=y=0\),此时状态不变
- 如果 \(x=y=0\),即左边界和上边界都没有插头,因为哈密顿回路要求每一个非障碍物的格子都要有两个插头,且这两个插头是连通的,即一定有一个出去然后另外一个回来,更新状态,由定义:\(x\leftarrow 1,y\leftarrow 2\)
- \(x=0,y\neq 0\),此时 \(y\) 这个插头有两种选择:向下、向右。向右时当前格子的右边界状态即为 \(y\),下边界状态为 \(0\);向下时下边界状态为 \(y\),右边界状态为 \(0\)
- \(x\neq 0,y=0\),同样地,此时 \(x\) 这个插头有两种选择:向下、向右。向右时当前格子的有边界状态为 \(x\),下边界状态为 \(0\);向下时下边界状态为 \(x\),右边界状态为 \(0\)
- \(x=y=1\),此时两个插头连通,当前格子的右、下边界状态都为 \(0\),且右边对应的状态需要修改,即此时两个状态 \(x,y\) 都有一个匹配插头,且都在右边,状态都为 \(2\),由于这两个插头已经连通,所以右边之前未匹配的插头开始匹配,即将右边两个匹配的插头的第一个插头状态修改为 \(1\)
- \(x=y=2\),同理,此时两个插头连通,当前格子的右、下边界状态都为 \(0\),且左边对应的状态需要修改,即此时两个状态 \(x,y\) 都有一个匹配插头,且都在左边,状态都为 \(1\),由于这两个插头已经连通,所以左边之前未匹配的插头开始匹配,即将右边两个匹配的插头的第二个插头状态修改为 \(2\)
- \(x=2,y=1\),此时两个插头连通,当前格子的右、下边界状态都为 \(0\),由于此时两个插头连通,但是对应的匹配的插头正好左边的匹配插头状态为 \(1\),右边的匹配插头状态为 \(2\),所以这时这样的状态不用修改
- \(x=1,y=2\),此时两个插头连通,但是对于当前 \(x\) 来说,其对应的匹配插头在右边,对于 \(y\) 来说,其对应的匹配插头在左边,这样两条路径要么相交,要么重合,由于两条路径相交的话就不再是一条哈密顿回路,所以只能重合,而重合说明当前遍历的格子一定得是最后一个合法的格子
设有效状态数量为 \(S\),则:
- 时间复杂度:\(O(n^3\times S)\)
代码
标签:插头,状态,匹配,边界,格子,2934,回路,DP
From: https://www.cnblogs.com/zyyun/p/16978234.html