链接:https://vjudge.net/problem/UVA-1601
这题折腾了好几天,就因为想用A*写,不过到现在我还没搞明白怎么回事。。
中间查了维基百科才知道,只有估价函数h满足三角不等式就可以避免将重复结点加入队列,但这个三角不等式指的是h(n) <= c(n,p) + h(p), c指的是实际的花费。不过在这题中我最终证明了自己的估计函数满足这个条件,但还是过不了。最终在网上找大佬的代码,发现中间的判重是每次取f较小的,也就是说会有重复的节点被遍历到。我最终尝试数学证明,只能证出终点不会被重复加入(这也是为什么判断队首等于目标就可以直接退出的原因),但其他的点证不出来。。最终尝试改了代码,发现确实输出了正确的数据。到这里人彻底蒙了。。
一交,还是WA。。哭了。终于最后突发灵感,发现memcmp比较了未定义的区域。又吃了一回教训。。
不过时间效率感人,实在不会优化,真的tcl。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cctype> #include <queue> #include <vector> //#define fre //#define DEBUG using namespace std; const int M = 4000000, mx[4] = {0, 0, 1, -1}, my[4] = {1, -1, 0, 0}; struct Point { int x, y; Point(int x = 0, int y = 0): x(x), y(y) {} bool operator == (const Point& b) const { return x == b.x && y == b.y; } int operator - (const Point& b) const { return abs(x - b.x) + abs(y - b.y); } }; struct Node { Point p[3]; int g, f; bool operator < (const Node& b) const { return f > b.f; } }; int base[6], n, graph[20][20]; Point tar[3]; int hashtable[M], pointhash[20][20], cnt; //pointhash按照点的个数编码,hashtable按照点集编码 vector<Point> avail[20][20]; void init() { base[0] = 1; for (int i = 1; i < 6; ++i) base[i] = base[i - 1] * 148; } inline int getpointhash(Point& t) { return pointhash[t.x][t.y]; } inline int gethash(Node& t) { int ans = 0; for (int i = 0; i < n; ++i) { ans += getpointhash(t.p[i]) * base[i]; } return ans; } inline int H(Node& t) { int ans = 0; for (int i = 0; i < n; ++i) { ans = max(ans, t.p[i] - tar[i]); } return ans; } bool inserthash(Node& t) { int h = gethash(t); t.f = t.g + H(t); if (hashtable[h] <= t.f) return false; else hashtable[h] = t.f; return true; } inline bool legal(Node& hd, Node& tl) { if (n == 3 && (tl.p[0] == tl.p[1] || tl.p[1] == tl.p[2] || tl.p[0] == tl.p[2])) return false; else if (n == 2 && tl.p[0] == tl.p[1]) return false; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { if (tl.p[i] == hd.p[j] && tl.p[j] == hd.p[i]) return false; } } return true; } int solve(Node& start) { bool f = true; priority_queue<Node> q; Node hd, tl; q.push(start); inserthash(start); int sze[3]; while (!q.empty()) { f = true; hd = q.top(), q.pop(); #ifdef DEBUG for (int i = 0; i < n; ++i) cout << hd.p[i].x << " " << hd.p[i].y << " "; cout << hd.g << endl; #endif //不能用memcmp,除非已统一初始化 for (int i = 0; i < n; ++i) { if (!(hd.p[i] == tar[i])) { f = false; break; } } if (f) return hd.g; tl.g = hd.g + 1; for (int i = 0; i < n; ++i) sze[i] = avail[hd.p[i].x][hd.p[i].y].size(); int kase = sze[0] - 1; for (int i = 1; i < n; ++i) { kase = kase * sze[i] + sze[i] - 1; }//神奇的编码方式。。 for (int i = 1; i <= kase; ++i) { int j = i; for (int k = n - 1; k >= 0; --k) { tl.p[k] = avail[hd.p[k].x][hd.p[k].y][j % sze[k]]; j /= sze[k]; } if (legal(hd, tl) && inserthash(tl)) { q.push(tl); } } } return -1; } int main() { #ifdef fre freopen("in.in", "r", stdin); freopen("out.txt", "w", stdout); #endif int w, h, ans; char c; init(); while (scanf("%d%d%d", &w, &h, &n) && w) { memset(hashtable, 0x3f3f3f3f, sizeof(hashtable)); memset(pointhash, 0, sizeof(pointhash)); memset(graph, 0, sizeof(graph)); for (int i = 0; i < 20; ++i) for (int j = 0; j < 20; ++j) avail[i][j].clear(); cnt = 0; Node start; start.g = 0; getchar(); for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) { if ((c = getchar()) == '#') graph[i][j] = 1; else { pointhash[i][j] = cnt++; if (isupper(c)) tar[c - 'A'] = Point(i, j); else if (islower(c)) start.p[c - 'a'] = Point(i, j); } } getchar(); } for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) { if (!graph[i][j]) { avail[i][j].push_back(Point(i, j)); int nx, ny; for (int k = 0; k < 4; ++k) { nx = i + mx[k], ny = j + my[k]; if (nx >= 1 && nx < h && ny >= 1 && ny < w && !graph[nx][ny]) avail[i][j].push_back(Point(nx, ny)); } } } } #ifdef DEBUG for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) cout << avail[i][j].size() << " "; cout << endl; } #endif start.f = start.g + H(start); printf("%d\n", solve(start)); } }