题目
给你一个 n × m n \times m n×m 的棋盘,有的格子是障碍,问共有多少条回路满足经过每个非障碍格子恰好一次。
如图, n = m = 4 n=m=4 n=m=4, ( 1 , 1 ) , ( 1 , 2 ) (1,1),(1,2) (1,1),(1,2) 是障碍,共有 2 2 2 条满足要求的回路。
输入格式
第一行包含两个整数 n , m n,m n,m。
接下来
n
n
n 行,每行包含一个长度为
m
m
m 的字符串,字符串中只包含 *
和 .
,其中 *
表示障碍格子,.
表示非障碍格子。
输出格式
输出一个整数,表示满足条件的回路数量。
数据范围
2 ≤ n , m ≤ 12 2 \le n,m \le 12 2≤n,m≤12
输入样例:
4 4
**..
....
....
....
输出样例:
2
思路
实现代码(详细注释)
#include <iostream>
#include <cstring>
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;
}