给你一个 \(n \times m\) 的棋盘,有的格子是障碍,问共有多少条回路满足经过每个非障碍格子恰好一次。
如图,\(n=m=4\),\((1,1),(1,2)\) 是障碍,共有 \(2\) 条满足要求的回路。
第一行包含两个整数 \(n,m\)。
接下来 \(n\) 行,每行包含一个长度为 \(m\) 的字符串,字符串中只包含 *
和 .
,其中 *
表示障碍格子,.
表示非障碍格子。
输出一个整数,表示满足条件的回路数量。
\(2 \le n,m \le 12\)
4 4
**..
....
....
....
2
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 50000, M = N * 2 + 7;
int n, m;
int endx, endy; // 记住最后一个有效格子便于封口
int g[20][20];
int q[2][N]; // 滚动数组,即有效状态在哈希表里面的下标
int cnt[N]; // 每次滚动时有效状态的数量
int h[2][M]; // 滚动哈希表
ll v[2][M]; // 哈希表存的方案数(对应的值)
int find(int cur, int x) { // 开放寻址法 求x在哈希表中的位置
int t = x % M;
while (h[cur][t] != -1 && h[cur][t] != x)
if (++t == M)
t = 0;
return t;
}
void insert(int cur, int state, ll w) { // 在当前滚动的状态内插入state,数量为w
int t = find(cur, state);
if (h[cur][t] == -1) {
h[cur][t] = state, v[cur][t] = w;
q[cur][++cnt[cur]] = t;
} else
v[cur][t] += w; // 状态在哈希表内已经存在
}
int get(int state, int k) { // 求第k个各自的状态,即四进制的第k位数字
return state >> k * 2 & 3;
}
int set(int k, int v) { // 构造四进制的第k位数字为v的数
return v * (1 << k * 2);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
char str[20];
scanf("%s", str + 1);
for (int j = 1; j <= m; ++j)
if (str[j] == '.') {
g[i][j] = 1;
endx = i, endy = j;
}
}
ll res = 0;
memset(h, -1, sizeof h);
int cur = 0;
insert(cur, 0, 1);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= cnt[cur]; ++j) // 处理分界线,0...... -> ......0
h[cur][q[cur][j]] <<=
2; // 每一次做下一行时,将上一行左移一位(相当于二进制下左移两位)
for (int j = 1; j <= m; ++j) {
int last = cur;
cur ^= 1, cnt[cur] = 0;
memset(h[cur], -1, sizeof h[cur]);
for (int k = 1; k <= cnt[last]; ++k) {
int state = h[last][q[last][k]];
ll w = v[last][q[last][k]];
int x = get(state, j - 1), y = get(state, j);
if (!g[i][j]) {
if (!x && !y)
insert(cur, state, w);
} else if (!x && !y) {
if (g[i + 1][j] && g[i][j + 1])
insert(cur, state + set(j - 1, 1) + set(j, 2), w);
} else if (!x && y) {
if (g[i][j + 1])
insert(cur, state, w);
if (g[i + 1][j])
insert(cur, state + set(j - 1, y) - set(j, y), w);
} else if (x && !y) {
if (g[i][j + 1])
insert(cur, state - set(j - 1, x) + set(j, x), w);
if (g[i + 1][j])
insert(cur, state, w);
} else if (x == 1 && y == 1) {
for (int u = j + 1, s = 1;; ++u) {
int z = get(state, u);
if (z == 1)
s++;
else if (z == 2) {
if (--s == 0) { // 找到了和他配对的2
insert(cur,
state - set(j - 1, x) - set(j, y) -
set(u, 1),
w);
break;
}
}
}
} else if (x == 2 && y == 2) {
for (int u = j - 2, s = 1;; --u) {
int z = get(state, u);
if (z == 2)
s++;
else if (z == 1) {
if (--s == 0) {
insert(cur,
state - set(j - 1, x) - set(j, y) +
set(u, 1),
w);
break;
}
}
}
} else if (x == 2 && y == 1) {
insert(cur, state - set(j - 1, x) - set(j, y), w);
} else if (
i == endx &&
j ==
endy) { // 封口,必须是最后一个合法的格子,否则路径不合法
res += w;
}
}
}
}
cout << res << '\n';
return 0;
}