插头 DP
插头 DP
定义
基于连通性状态压缩的 DP.
一个方向的插头存在表示这个格子在这个方向可以与外面相连。
状态
一个 \(n \times m(n, m \le 12)\) 的棋盘,有的格子是障碍,问共有多少满足要求的回路?
本题中,所有非障碍格子一定是从一个插头进、一个插头出,刚好用两个插头,方案数为 \(C_4^2=6\) 种.
-
逐行递推,按照从上到下的顺序依次考虑每一行。
-
分析第 \(i\) 行那些信息对第 \(i+1\) 行有影响,记录第 \(i\) 行的每个格子是否有下插头,这决定了第 \(i+1\) 行的每个格子是否有上插头。
-
仅仅记录插头是否存在是不够的,可能导致出现多个回路,还要记录第 \(i\) 行的 \(n\) 个格子的连通情况。
-
注意到任何时候只有轮廓线上方与其直接相连的格子和插头才会对轮廓线以下的格子产生直接的影响。
-
定义 \(f(i, S_0, S_1)\) 表示前 \(i\) 行,第 \(i\) 行的 \(n\) 个格子是否具有下插头第 \(n\) 位二进制数为 \(S_0\),第 \(i\) 行的 \(n\) 个格子之间的连通性为 \(S_1\) 的方案总数。
-
给每个格子标记一个数,同一个连通块标记相同的数,比如 \({2, 2, 1, 1}\) 表示格子 \(1, 2\) 和 \(3, 4\) 分别在两个连通块内。通常使用最小表示法对格子进行标记。
-
一种最小表示法为:同一个联通块用相同数字表示,所有的障碍格子标记为 \(0\),从第一个非障碍格子开始不断向四周扩展。
-
使用第一种表示法,上图的三个状态依次表示为:
\(f(1, (0011)_2, \{0, 0, 1, 1\}), f(2, (1111)_2, \{1, 1, 2, 2\}), f(3, (1001)_2, \{1, 1, 1, 1\})\).
优化
- 可以发现如果轮廓线上方的 \(n\) 个格子中某个格子没有下插头,那么它就不会再与轮廓线以下的格子直接相连,他的连通性对轮廓线以下的格子没有影响,就成了“冗余信息”。
- 不妨将记录格子的连通性改为记录插头的连通性,如果这个插头存在,那么就标记这个插头对应的格子的连通标号,如果这个插头不存在,就标记为 \(0\).
转移
- 假设从第 \(i\) 行转移到第 \(i+1\) 行,可以枚举第 \(i+1\) 行的每个格子的状态,对于任何一个非障碍格子,它是否有上插头和左插头已知,因此最多只有两种情况,状态的转移数 \(\le 2^n\).
- 枚举完第 \(i+1\) 行每个格子的状态后,需要计算第 \(i+1\) 行 \(n\) 个格子之间连通性的最小表示。通常可以用并查集的 \(fa\) 数组对其标号或重新执行一次 DFS/BFS 染色,时间复杂度为 \(O(n)\),最后将格子的连通性转移到插头的连通性上。
- 注意:在转移的过程中,为了避免出现多个联通块,除了最后一行,每行中必须有至少一个下插头。
逐格递推
转移
程序实现