writeup是说:插头dp模板题


http://westerns.tokyo/writeups/0ctf2018quals.html#maya-game


代码添加注释:

#include <iostream>
#include <vector>
#include <cassert>
#include <algorithm>
#include <map>
using namespace std;

int N, M;

vector<string> MAP;

const int dx[4] = {0, 1, 0, -1};
const int dy[4] = {1, 0, -1, 0};

vector<vector<vector<map<vector<int>,int64_t> > > > dp;

void normalize(std::vector<int> &connection, int &left) {
    vector<int> mp(connection.size() + 2, -1);
    //vector<int> a(10, 1);
    //声明一个初始大小为10,且初始值为1
    //memset(mp,-1)
    mp[0] = 0;
    int k = 1;
    for (int i = 0; i < connection.size(); i++) {
        if (connection[i] != 0) {
            assert(connection[i] < mp.size());
            if (mp[connection[i]] == -1) {
                mp[connection[i]] = k++;
            }
        }
    }
    if (left != 0) {
        if (mp[left] == -1) {
            mp[left] = k;
        }
    }

    for (int i = 0; i < connection.size(); i++) {
        connection[i] = mp[connection[i]];
    }
    left = mp[left];
}

int64_t rec(int y, int x, const std::vector<int> connection, int left) {
    assert(connection.size() == N);
    if (y == N) {
        // 終了条件
        for (int i = 0; i < N; i++) {
            if (connection[i] != 0) {
                // 上に接続がある: Fail
                // 上有连接:fail
                return 0;
            }
        }
        // 上に接続がなければひとまず成功とする
        // 若没有上连接,则先成功
        // 最后一列都成功匹配
        return 1;
    }
    //记忆化搜索
    //y:列坐标
    //x:横坐标
    //left:剩下的是哪一个没有匹配
    //connection:上面的连接情况
    if (dp[y][x][left].count(connection)) {
        //count统计等于某个值的对象的个数
        return dp[y][x][left][connection];
    }

    cout << "REC: (" << x << ", " << y << ") : " << left << " {";
    for(int i = 0; i < connection.size(); i++) {
        cout << connection[i] << ", ";
    }
    cout << "}" << endl;
    /*
    cout << "\tcc: [";
    for(int i = 0; i < connection_count.size(); i++) {
        cout << connection_count[i] << ", ";
    }
    cout << endl;
    */
    int ny = y, nx = x + 1;
    //下一个节点
    if (nx == N) {
        ny++;
        nx = 0;
    }
    // cout << "s:" << y << "," << x << endl;

    int64_t ret = 0;
    const char cur = MAP[y][x];
    // cur是下一个字符
    if (cur == '#') {
        if (left != 0) {
            // 左側からの接続があるので駄目
            // 因为有来自左边的连接,所以不行
            ret = 0;
        } else if (connection[0] != 0) {
            // 上からの接続があるので駄目
            // 上面有连接,所以不行
            ret = 0;
        } else {
            // 次のコネクションを作る
            // 制作下面的关系
            // 線は引かずに次に進むしかない
            // 只能继续走下去
            vector<int> next_connection = connection;
            next_connection.erase(next_connection.begin());
            //删除第一个元素
            next_connection.push_back(0);
            //在末尾添加一个0
            ret = rec(ny, nx, next_connection, 0);
        }
    } else if (cur == 'X') {
        // 次のコネクションを作る
        // 制作下面的关系
        if (left != 0 && connection[0] != 0) {
            // 上と左から来ている場合は二本引かれるためアウト
            // 在上面和左来的情况下,因为被吸引而出局
            ret = 0;
        } else if (left != 0) {
            assert(connection[0] == 0);
            // 左からのみ来ている場合
            // 从左边来的情况
            // 接続を追加する
            // 追加连接
            vector<int> next_connection = connection;
            next_connection.erase(next_connection.begin());
            next_connection.push_back(0);
            //删除第一个元素,末尾添加一个0
            int next_left = 0;
            normalize(next_connection, next_left);
            ret = rec(ny, nx, next_connection, next_left);
        } else if (connection[0] != 0) {
            assert(left == 0);
            // 上からのみ来ている場合
            // 从上面来的情况
            vector<int> next_connection = connection;
            next_connection.erase(next_connection.begin());
            next_connection.push_back(0);
            int next_left = 0;
            normalize(next_connection, next_left);
            ret = rec(ny, nx, next_connection, next_left);
        } else {
            // 右か下に接続する
            // 连接到右下角
            // 右に接続する場合
            int next_number = max(left, *max_element(connection.begin(), connection.end())) + 1;
            ret = 0;
            if (x < N - 1) {
                vector<int> next_connection = connection;
                int next_left = next_number;
                next_connection.erase(next_connection.begin());
                next_connection.push_back(0);
                // cout << x << "," << y << "," << left << ":" << (next_connection_count.size()) << "," << (next_number + 1) << endl;
                normalize(next_connection, next_left);
                ret += rec(ny, nx, next_connection, next_left);
            }
            if (y < N - 1) {
                // 下に接続する
                // 连接到下面
                vector<int> next_connection = connection;
                int next_left = 0;
                next_connection.erase(next_connection.begin());
                next_connection.push_back(next_number);
                normalize(next_connection, next_left);
                ret += rec(ny, nx, next_connection, next_left);
            }
        }
    } else if (cur == '.') {
        // 次のコネクションを作る
        // 制作下面的关系
        // 上→下
        if (connection[0] != 0 && left == 0 && y < N - 1) {
            // 上からの接続があり右側から接続がない
            // 从上面有连接,右边没有连接
            vector<int> next_connection = connection;
            int next_left = 0;
            next_connection.erase(next_connection.begin());
            next_connection.push_back(connection[0]);
            ret += rec(ny, nx, next_connection, next_left);
        }
        // 上から右
        // 从上向右
        if (connection[0] != 0 && left == 0 && x < N - 1) {
            // 上からの接続があり右側から接続がない
            // 从上面有连接,右边没有连接
            vector<int> next_connection = connection;
            int next_left = connection[0];
            next_connection.erase(next_connection.begin());
            next_connection.push_back(0);
            normalize(next_connection, next_left);
            ret += rec(ny, nx, next_connection, next_left);
        }

        // 左→下
        if (connection[0] == 0 && left != 0 && y < N - 1) {
            // 上からの接続があり右側から接続がない
            // 从上面有连接,右边没有连接
            vector<int> next_connection = connection;
            int next_left = 0;
            next_connection.erase(next_connection.begin());
            next_connection.push_back(left);
            normalize(next_connection, next_left);
            ret += rec(ny, nx, next_connection, next_left);
        }
        // 左から右
        // 从左到右
        if (connection[0] == 0 && left != 0 && x < N - 1) {
            vector<int> next_connection = connection;
            int next_left = left;
            next_connection.erase(next_connection.begin());
            next_connection.push_back(0);
            normalize(next_connection, next_left);
            ret += rec(ny, nx, next_connection, next_left);
        }
        // 左上
        if (connection[0] != 0 && left != 0) {
            if (connection[0] == left) {
                // 既に接続ずみなので置けない
                // 已经连接不了,放不下
            } else {
                // 左と上を接続
                // 把左边和上面连接起来
                vector<int> next_connection = connection;
                int next_left = 0;
                next_connection.erase(next_connection.begin());
                next_connection.push_back(0);
                // Merge
                // 合并
                for (int i = 0; i < N; i++) {
                    if (next_connection[i] == left) {
                        next_connection[i] = connection[0];
                    }
                }
                // Normalize
                // 
                normalize(next_connection, next_left);
                ret += rec(ny, nx, next_connection, next_left);
            }
        }
        // 右上
        if (connection[0] == 0 && left == 0 && x < N - 1 && y < N - 1) {
            int next_number = max(left, *max_element(connection.begin(), connection.end())) + 1;
            ret = 0;
            vector<int> next_connection = connection;
            int next_left = next_number;
            next_connection.erase(next_connection.begin());
            next_connection.push_back(next_number);
            normalize(next_connection, next_left);
            ret += rec(ny, nx, next_connection, next_left);
        }
        // 何もしない
        // 什么也不干
        if (connection[0] == 0 && left == 0) {
            vector<int> next_connection = connection;
            int next_left = 0;
            next_connection.erase(next_connection.begin());
            next_connection.push_back(0);
            normalize(next_connection, next_left);
            ret += rec(ny, nx, next_connection, next_left);
        }
    } else {
        //std::cout << "Invalid Error" << endl;
        //throw std::runtime_error("Invalid Map data");
        cout << "Input Error" << endl;
    }
    return dp[y][x][left][connection] = ret;
}

//
// Upとの接続状況
// 与up的连接状况
//
// Yes
// +-----+
// |+---+|
// ||+-+||
// |||#|X|
// XXX#+-+
//
// Split [1,1,1,1,1,1,1]
//
int main() {
    freopen("input.txt","r",stdin);
    while(scanf("%d%d",&N,&M)!=EOF){
        //cin >> N >> M;
        MAP.clear();
        dp.clear();
        MAP.resize(N);
        for (int i = 0; i < N; i++) {
            cin >> MAP[i];
        }
        //X:0->N-1
        //Y:0->N-1
        dp.resize(N);
        for( int i = 0; i < dp.size(); i++) {
            dp[i].resize(N);
            for(int j = 0 ; j < dp[i].size(); j++) {
                dp[i][j].resize(N + 3);
            }
        }
        vector<int> connection(N);
        //connection.clear();
        int64_t ret = rec(0, 0, connection, 0);
        cout << ret << endl;
    }
    return 0;
}


附上数据:

4 4
XX..
....
..##
.X.X

7 6
.......
...X.X.
.#..X..
....X..
..#....
X.X...#
.......

4 6
#XX.
X..X
XX#.
..#.

4 6
.XX#
X#.X
X.X.
....

3 4
#XX
.X.
#.X

4 6
X.XX
X.#X
..X.
#...

5 6
..#..
..#.X
...XX
..X..
X..X.

4 6
X#.X
..X.
..X.
#.XX

4 4
...X
X.X#
#...
X..#