POJ 1475 推箱子 解题报告

做完八数码问题之后对搜索问题有了一定认识,也很有兴趣,就开始做POJ1475。走了不少弯路,费了很多时间,总结一下。
题目要求输出一条push次数最少情况下total最少的路径,开始我以为是要求总移动最少,就写了个双向BFS,将walk和push看做同等操作,等程序跑起来之后才发现不是题目要求的。而且反向时我只考虑了箱子被搬动的情况,这样反向搜索时可能碰不到正向,就退化为周界搜索。
中间我用单向BFS ac了一次,用的是vc,非常慢。在搞清题意后我重新写了双向BFS。这次两边用的都是优先队列,优先扩展少push,等push下少total的节点。最后发现一个测试数据我得到了total数比答案多2的输出。这时我才慢慢认识到双向BFS走不通,因为即便用的优先队列,但total层数不想BFS那样只有两层,而可能有很多层,这时就不能保证等push下total最少。
刚好看到嵌套BFS的做法,知道了推箱子问题的关键。推是主要的,walk只能围绕着push进行,否则walk缺乏目标,会搜索很多没意义的节点。如果采用嵌套BFS,第一层是push的BFS,在push之后给walk设定目标BFS。这样比不嵌套时有很大的效率提升(考虑20*20完全空的布局)。
我的嵌套BFS做法是保证队列总是有序的,即总是按照少push优先,等push少total优先的规则排序的,但不采用优先队列。第一层BFS已经保证了第一条优先规则,第二条则是通过将walk BFS产生的有序的可push节点序列与原来的push的BFS队列合并,就能保证每次push时一定选择符合规则的最优先节点扩展。要让有序队列高效地合并,我的队列就采用链表(使用链表的主要缺点是多占了内存,效率也有影响)。此外还需要初始化队列、walk目标管理等操作。
我的做法很繁琐,也不便于调试,最后还是下了官方数据调试通过的,实在惭愧。我看了POJ上排名第一的做法,他的做法很简洁,嵌套之后找total最小的,顶多只是多扩一层push的节点,不用初始化队列,不用管理目标,不使用链表,walk和push还可以合并在一起,实现起来只有不到200行。当然分析和编写、测试的代码还是很有意思的。
我感觉代码写长了非常不利于维护,对之前每个walk,push操作展开为八个函数,感觉非常可笑,可能是为了一点的效率,使得程序非常不好测试调试,有影响思路展开,在应用中这种优化放到后期都可以。稍复杂些,编写可靠的代码就十分不易。矫枉过正,我的代码中甚至连根据当前状态和方向反推节点的操作都没写,直接保留了个from。
以下是我的代码:

#include <cstdio> #include <iostream> #include <list> #include <algorithm> #include <queue> using namespace std; int r, c; char board[20][20]; struct tagPoint { char x; char y; tagPoint& operator = (const tagPoint& rhs) { x = rhs.x; y = rhs.y; return *this; } bool operator == (const tagPoint& rhs) const { return x == rhs.x && y == rhs.y; } bool operator != (const tagPoint& rhs) const { return x != rhs.x || y != rhs.y; } bool invalid(void) const { return x < 0 || x >= c || y < 0 || y >= r || board[y][x] == '#'; } }; struct tagState { tagPoint S; tagPoint B; tagState& operator = (const tagState& rhs) { S = rhs.S; B = rhs.B; return *this; } bool operator == (const tagState& rhs) const { return S == rhs.S && B == rhs.B; } bool operator != (const tagState& rhs) const { return S != rhs.S || B != rhs.B; } bool is(char sx, char sy, char bx, char by) const { return S.x == sx && S.y == sy && B.x == bx && B.y == by; } }; struct tagData { tagState from; int direct; int push; int total; }; struct tagWalkDest { short count; tagPoint dst[4]; bool Search(tagPoint pos) const { for (int i = 0; i < 4; ++i) { if (pos == dst[i]) { return true; } } return false; } }; tagPoint T; tagState invalidState = { {-1, -1}, {-1, -1}}; int push_dirs[] = {'N', 'S', 'W', 'E'}; char push_x[] = {0, 0, -1, 1}; char push_y[] = {-1, 1, 0, 0}; int walk_dirs[] = {'n', 's', 'w', 'e'}; char walk_x[] = {0, 0, -1, 1}; char walk_y[] = {-1, 1, 0, 0}; struct tagTable { tagData& operator [] (tagState state) { return tab[state.S.y][state.S.x][state.B.y][state.B.x]; } tagData tab[20][20][20][20]; }; tagTable table; tagWalkDest walk_dst[20][20]; //push次数相同时在队列中让total次数少的优先 bool cmp_state(const tagState& lhs, const tagState& rhs) { return table[lhs].total < table[rhs].total; } tagState GetWalkDest(tagState pos, int i) { //箱子在人某个方向上,由箱子反求出人的位置 pos.S.x = pos.B.x - push_x[i]; pos.S.y = pos.B.y - push_y[i]; //如果现在人被挤到了墙的位置,则返回invalidState if (pos.S.invalid()) { return invalidState; } return pos; } tagState Walk(tagState state, int i) { //人往某个方向移动 state.S.x += walk_x[i]; state.S.y += walk_y[i]; //如果人现在被挤到了墙的位置或占了B的位置,则返回invalidState if (state.S.invalid() || state.S == state.B) { return invalidState; } return state; } void InitSearch(list<tagState> &lst, tagState pos) { tagWalkDest dst; tagState child; queue<tagState> q; int i; //初始化哈希表 memset(table.tab, 0xff, sizeof(table)); //将初始位置放入哈希表中 table[pos].from = invalidState; table[pos].direct = 0; table[pos].push = 0; table[pos].total = 0; //如果pos本身就是可push的,则放入队列中 if (abs(pos.S.x - pos.B.x) + abs(pos.S.y - pos.B.y) == 1) { lst.push_back(pos); } //构建最多三个walk目标 memset(&dst, 0xff, sizeof(dst)); dst.count = 0; for (i = 0; i < 4; ++i) { child = GetWalkDest(pos, i); if (*(int*)&child != -1 && child != pos) { dst.dst[dst.count] = child.S; ++dst.count; } } //将pos放入广度优先队列中搜索 if (!dst.count) { return; } q.push(pos); while (!q.empty()) { pos = q.front(); q.pop(); if (dst.count == 0) { continue; } for (i = 0; i < 4; ++i) { child = Walk(pos, i); if (child != invalidState) { if (table[child].direct == -1) { q.push(child); table[child].from = pos; table[child].direct = walk_dirs[i]; table[child].push = 0; table[child].total = table[pos].total + 1; if (dst.Search(child.S)) { --dst.count; lst.push_back(child); if (!dst.count) { return; } q.push(child); table[child].from = pos; table[child].direct = walk_dirs[i]; table[child].push = 0; table[child].total = table[pos].total + 1; } } } } } } tagState Push(tagState state, int i) { //人往某个方向推箱子,人先动 state.S.x += push_x[i]; state.S.y += push_y[i]; //如果人不占B的位置或占了墙的位置,返回invalidState if (state.B != state.S || state.S.invalid()) { return invalidState; } //现在箱子移动 state.B.x += push_x[i]; state.B.y += push_y[i]; //如果箱子占了墙的位置,返回invalidState if (state.B.invalid()) { return invalidState; } return state; }; void walkBFS(list<tagState>& tomerge, list<tagState>& q, tagState state, int level) { long size; tagState child; int i; //如果state是目标之一,相关目标计数减一 if (walk_dst[state.B.y][state.B.x].Search(state.S) && walk_dst[state.B.y][state.B.x].count > 0) { --walk_dst[state.B.y][state.B.x].count; } q.push_back(state); if (level <= 0) { return; } //逐层扩展walk for (size = q.size(); size && level; size = q.size(), --level) { for (; size; --size) { state = q.front(); q.pop_front(); if (walk_dst[state.B.y][state.B.x].count == 0) { continue; } for (i = 0; i < 4; ++i) { child = Walk(state, i); if (*(int*)&child != -1) { if (table[child].direct == -1) { q.push_back(child); table[child].from = state; table[child].direct = walk_dirs[i]; table[child].push = table[state].push; table[child].total = table[state].total + 1; if (walk_dst[child.B.y][child.B.x].Search(child.S)) { --walk_dst[child.B.y][child.B.x].count; tomerge.push_back(child); } } } } } } } void uptrace(tagState state) { if (table[state].direct) { uptrace(table[state].from); putchar(table[state].direct); } } void Search(tagState pos) { tagState state, child, next; long size; int i, j; list<tagState> lst, tomerge; list<tagState>::iterator itr, itrnext; list<tagState> q; int level; //初始化搜索,得到可push的四个状态 InitSearch(lst, pos); //开始对push逐层搜索 for (size = lst.size(); size; size = lst.size()) { for (; size; --size) { state = lst.front(); lst.pop_front(); //扩展孩子 for (i = 0; i < 4; ++i) { child = Push(state, i); if (~*(int*)&child) { //如果找到,即Box到达Target位置 if (child.B.x == T.x && child.B.y == T.y) { //回溯输出 #if 1 uptrace(state); putchar(push_dirs[i]); #else printf("Pushbox times:%d Free_walk times:%d", table[state].push + 1, table[state].total - table[state].push); #endif printf("/n/n"); return; } //孩子放入表中,压入bfs队列 if (table[child].direct == -1) { table[child].from = state; table[child].direct = push_dirs[i]; table[child].push = table[state].push + 1; table[child].total = table[state].total + 1; lst.push_back(child); } } } } //目标清零 memset(walk_dst, -1, sizeof(walk_dst)); for (i = 0; i < r; ++i) for (j = 0; j < c; ++j) { walk_dst[i][j].count = 0; } //扩展完一层后,要再经walk得到更多同一层可push的节点 for (itr = lst.begin(), size = lst.size(); size; --size, ++itr) { state = *itr; //确定好walk的目标,而且这些目标合法且一定没有被爬过 if (walk_dst[state.B.y][state.B.x].count == 0) { for (i = 0; i < 4; ++i) { child = GetWalkDest(state, i); if (*(int*)&child != -1 && table[child].direct == -1) { walk_dst[state.B.y][state.B.x].dst[walk_dst[state.B.y][state.B.x].count] = child.S; ++walk_dst[state.B.y][state.B.x].count; } } } //开始朝着确定好的目标walk //查看下一个元素的total总数,从而确定可以walk几层 itrnext = itr; ++itrnext; if (itrnext == lst.end()) { level = INT_MAX; } else { next = *itrnext; level = table[next].total - table[state].total; }; walkBFS(tomerge, q, state, level); } //合并两个有序队列,从而保证同push次数total优先 q.clear(); lst.merge(tomerge, cmp_state); } printf("Impossible./n/n"); } int main() { long i, j, t; tagState pos; //freopen("c://testcase.in.txt", "rb", stdin); for (t = 1; ;++t) { scanf("%d%d", &r, &c); if (!r && !c) { return 0; } for (i = 0; i < r; ++i) { for (j = 0; j < c; ++j) { cin >> board[i][j]; if (board[i][j] == 'S') { pos.S.x = j; pos.S.y = i; } else if (board[i][j] == 'B') { pos.B.x = j; pos.B.y = i; } else if (board[i][j] == 'T') { T.x = j; T.y = i; } } } printf("Maze #%d/n", t); Search(pos); } }

你可能感兴趣的:(struct,table,search,iterator,扩展,DST)