Loading

插头dp学习笔记

题目

给你一个 \(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;
}
posted @ 2023-06-24 11:48  popcoount  阅读(35)  评论(1)    收藏  举报  来源