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..#