UVa1302/LA2417 Gnome Tetravex
UVa1302/LA2417 Gnome Tetravex
- 题目链接
- 题意
- 输入格式
- 输出格式
- 分析
- AC 代码
题目链接
本题是2001年icpc亚洲区域赛上海赛区的A题
题意
有 n×n(1≤n≤5)n\times n\;(1 \le n \le 5)n×n(1≤n≤5) 张正方形卡片,每张卡片分为上下左右四个三角形,三角形内各有一个 0~9 的数字,问能否把这些卡片排成 $n\times n$ 的方阵使得相邻卡片上相邻的三角形内数字相同。
输入格式
输入包含多组数据。每组数据第一行为 1 个整数 n(1≤n≤5)n\;(1 \le n \le 5)n(1≤n≤5),接下来 n×nn\times nn×n 行每行 4 个数字,依次代表每张卡片上三角形、右三角形、下三角形、左三角形内的数字。输入结束标志为 n=0。
输出格式
对于每组数据,如果有解则输出一行 Game k: Possible,无解则输出一行 Game k: Impossible,其中 k 为测试数据的编号(编号从 1 开始)。每组数据输出一行后还需要再输出一个空行。
分析
搜索题常见解法有双向BFS(Bi-directional BFS)、迭代加深搜索(IDDFS)、启发式搜索(比如A*等)。如果用双向BFS,那么 n=5n=5n=5时,状态数介于 C2510C_{25}^{10}C2510~A2510A_{25}^{10}A2510 之间,会超时。也不需要迭代加深搜索,因为有解时步数一定是 n×nn\times nn×n。那么需要往启发式搜索的方向去想,基本框架是 DFS,主要难点在于设计启发式函数 h。
每张卡片分为上下左右四个三角形,其内各自包含一个数字,但是卡片要作为一个整体去摆放。不妨将其弱化使得摆放限制更宽松:将每张卡片裁剪成上下左右四个三角形,然后再去以三角形为单位进行摆放,这时候如果无解则原问题必然无解。因此可以用这个弱化的摆放限制作为启发式函数 h:每次 DFS 搜索前,将尚未枚举到的那些卡片的上下左右四个三角形内数字按数值和方位分类统计,除了边界三角形外,其他三角形必须是数字相同的(上、下或者左、右)两两配对,也就是说剩下的卡片中不能配对的上下左右三角形数量不能大于剩下区域对应的上下左右边长,并且需要在统计时先将已摆放区域的下轮廓线上的三角形数字对应减掉。
AC 代码
#include <iostream>
using namespace std;#define M 25
int l[M], r[M], t[M], b[M], f[5], cl[10], cr[10], ct[10], cb[10], m, n, kase = 0; bool vis[M];bool h(int r, int c) {bool ret = true;if (c > 0) {if (cl[::r[f[c-1]]]-- == 0) ret = false;if (r+1 < n) for (int i=0; i<c; ++i) if (ct[b[f[i]]]-- == 0) ret = false;}if (r > 0) for (int i=c; i<n; ++i) if (ct[b[f[i]]]-- == 0) ret = false;int sr = n-r, sl = c > 0 ? sr-1 : sr, sb = r+1 < n ? n : n-c, st = r > 0 ? 0 : n-c;for (int i=0; i<10; ++i) {if (cl[i] > cr[i]) sl -= cl[i] - cr[i];if (cl[i] < cr[i]) sr -= cr[i] - cl[i];if (ct[i] > cb[i]) st -= ct[i] - cb[i];if (ct[i] < cb[i]) sb -= cb[i] - ct[i];}if (c > 0) {++cl[::r[f[c-1]]];if (r+1 < n) for (int i=0; i<c; ++i) ++ct[b[f[i]]];}if (r > 0) for (int i=c; i<n; ++i) ++ct[b[f[i]]];return ret && sl >= 0 && sr >= 0 && st >= 0 && sb >= 0;
}bool dfs(int r = 0, int c = 0) {if (r == n) return true;if (c == n) return dfs(r+1);if (h(r, c)) for (int i=0; i<m; ++i) if (!vis[i]) {if ((r > 0 && b[f[c]] != t[i]) || (c > 0 && ::r[f[c-1]] != l[i])) continue;int e = f[c]; f[c] = i; vis[i] = true; --cl[l[i]]; --cr[::r[i]]; --ct[t[i]]; --cb[b[i]];if (dfs(r, c+1)) return true;f[c] = e; vis[i] = false; ++cl[l[i]]; ++cr[::r[i]]; ++ct[t[i]]; ++cb[b[i]];}return false;
}void solve() {m = n*n;for (int i=0; i<10; ++i) cl[i] = cr[i] = ct[i] = cb[i] = 0;for (int i=0, j=m; i<j; ++i)cin >> t[i] >> r[i] >> b[i] >> l[i], vis[i] = false, ++cl[l[i]], ++cr[r[i]], ++ct[t[i]], ++cb[b[i]];cout << "Game " << ++kase << (dfs() ? ": Possible" : ": Impossible") << endl << endl;
}int main() {while (cin >> n && n) solve();return 0;
}