標籤:
題目大意
推箱子遊戲的基本玩法,求將箱子推到目的地的推動次數最少(並不是人移動總次數)的人移動路徑。
題目分析
求最短路徑的搜尋問題,使用BFS。注意題目求的是 推動次數最少,因此將箱子移動作為狀態,求箱子移動最少次數到達目的地的路徑,然後在該路徑的拐點基礎上再次進行 對人移動的BFS搜尋。
即BFS套BFS。
和所有的搜尋問題,動態規劃問題一樣,對程式的每個步驟,都要明確、詳盡(主要是可以完整儲存所需要的資訊,而不是資料量大)的選項組。本題目中的每個cell,都可以從四個不同的方向進入,因此選項組時候,需要記錄每個點,及其它是從哪個方向進入。
我在實現的時候,先按照第一層BFS搜尋,找到箱子的最短移動路徑,中間記錄每個點的前一個點,最後再反向推算出箱子的正向移動路徑; 然後找出拐點處的人應該移動的位置,再次進行BFS,中間也是儲存了每個點的前一個點,最後反向推算出正向路徑。這樣就導致代碼長度很長 →_→ !!!
這樣做,在每個節點需要儲存的資料量比較大的時候比較划算,但是每個節點需要儲存的就只是一個字元,而總共不超過400個點,即使都用一個string儲存下來,記憶體能應付過來。看網上有人是採用這種方式實現的,確實很精巧!參見POJ_1475
#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<queue>#include<string.h>#include<stack>using namespace std;#define MAX_CASTLE_SIZE 24#define INFINITE 1 << 30char gCastle[MAX_CASTLE_SIZE][MAX_CASTLE_SIZE];bool gWalkVisited[MAX_CASTLE_SIZE][MAX_CASTLE_SIZE];int gRow, gCol;int gBoxPos[2];int gPeoplePos[2];int gTargetPos[2];int gMoveInc[5][2] = { { 1, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 }, { 0, 1 } };char gWalkDirChar[5] = { ‘t‘, ‘w‘, ‘s‘, ‘n‘, ‘e‘ };char gPushDirChar[5] = { ‘T‘, ‘W‘, ‘S‘, ‘N‘, ‘E‘ };struct PushNode{int pre_row;int pre_col;int pre_dir;PushNode(int p_r = 0, int p_c = 0, int p_d = 0) :pre_row(p_r), pre_col(p_c), pre_dir(p_d){};};PushNode gPushNodes[MAX_CASTLE_SIZE][MAX_CASTLE_SIZE][4];struct WalkNode{int pre_row;int pre_col;int dir_from_pre;WalkNode(int p_r = 0, int p_c = 0, int d = 0) :pre_row(p_r), pre_col(p_c), dir_from_pre(d){};};WalkNode gWalkNodes[MAX_CASTLE_SIZE][MAX_CASTLE_SIZE];struct PeopleMoveNode{bool arrive_by_walk;int row;int col;int dir_to_next;int box_x;int box_y;PeopleMoveNode(bool w = true, int r = 0, int c = 0, int d = 0, int bx = 0, int by = 0) :arrive_by_walk(w), row(r), col(c), dir_to_next(d), box_x(bx), box_y(by){};void SetInfo(bool w, int r, int c, int d, int bx, int by){arrive_by_walk = w;row = r;col = c;dir_to_next = d;box_x = bx;box_y = by;}};stack<PeopleMoveNode> gPushNodestack;void Input(){memset(gCastle, ‘#‘, sizeof(gCastle));for (int row = 1; row <= gRow; row++){getchar();for (int col = 1; col <= gCol; col++){scanf("%c", &gCastle[row][col]);if (gCastle[row][col] == ‘B‘){gBoxPos[0] = row;gBoxPos[1] = col;}if (gCastle[row][col] == ‘S‘){gPeoplePos[0] = row;gPeoplePos[1] = col;}if (gCastle[row][col] == ‘T‘){gTargetPos[0] = row;gTargetPos[1] = col;}}}}void TraceWay(int start_x, int start_y, int cur_x, int cur_y, bool walk){int pre_x = gWalkNodes[cur_x][cur_y].pre_row;int pre_y = gWalkNodes[cur_x][cur_y].pre_col;if (start_x == cur_x && start_y == cur_y){//char dir = walk ? gWalkDirChar[gWalkNodes[cur_x][cur_y].dir_from_pre] : gPushDirChar[gWalkNodes[cur_x][cur_y].dir_from_pre];//printf("%c", dir);return;}TraceWay(start_x, start_y, pre_x, pre_y, walk);char dir = walk ? gWalkDirChar[gWalkNodes[cur_x][cur_y].dir_from_pre] : gPushDirChar[gWalkNodes[cur_x][cur_y].dir_from_pre];printf("%c", dir);}void FindMinStepWay(int start_x,int start_y, int end_x, int end_y, int box_x, int box_y){memset(gWalkNodes, 0, sizeof(gWalkNodes));queue<pair<int, int> > Q;pair<int, int> pos_pair(start_x, start_y);gWalkNodes[start_x][start_y].pre_row = -1;gWalkNodes[start_x][start_y].pre_col = -1;Q.push(pos_pair);while (!Q.empty()){pos_pair = Q.front();Q.pop();if (pos_pair.first == end_x && pos_pair.second == end_y){TraceWay(start_x, start_y, end_x, end_y, true);return;}for (int i = 1; i <= 4; i++){int next_x = pos_pair.first + gMoveInc[i][0];int next_y = pos_pair.second + gMoveInc[i][1];if (gCastle[next_x][next_y] != ‘#‘ && !gWalkNodes[next_x][next_y].pre_row && (next_x != box_x || next_y != box_y)){gWalkNodes[next_x][next_y].pre_row = pos_pair.first;gWalkNodes[next_x][next_y].pre_col = pos_pair.second;gWalkNodes[next_x][next_y].dir_from_pre = i;Q.push(pair<int, int>(next_x, next_y));}}}}//人從 people_x, people_y 出發,能否walk到(不能推動箱子) pos_x, pos_y, 箱子位於 box_x, box_ybool CanPushFromPos(int pos_x, int pos_y, int people_x, int people_y, int box_x, int box_y){if (gCastle[pos_x][pos_y] == ‘#‘){return false;}if (pos_x == people_x && pos_y == people_y){return true;}memset(gWalkVisited, false, sizeof(gWalkVisited));gWalkVisited[people_x][people_y] = true;gWalkVisited[box_x][box_y] = true;queue<pair<int, int> >Q;pair<int, int> pos_pair(people_x, people_y);Q.push(pos_pair);while (!Q.empty()){pos_pair = Q.front();Q.pop();if (pos_pair.first == pos_x && pos_pair.second == pos_y){return true;}for (int i = 1; i <= 4; i++){int next_x = pos_pair.first + gMoveInc[i][0];int next_y = pos_pair.second + gMoveInc[i][1];if (gWalkVisited[next_x][next_y] || gCastle[next_x][next_y] == ‘#‘){continue;}gWalkVisited[next_x][next_y] = true;Q.push(pair<int, int>(next_x, next_y));}}return false;}void SetNodeStack(){while (!gPushNodestack.empty()){gPushNodestack.pop();}int cur_x = gTargetPos[0], cur_y = gTargetPos[1];int last_dir = -1;for (int i = 1; i <= 4; i++){if (gPushNodes[cur_x][cur_y][i].pre_row){last_dir = i;break;}}int pre_x = gPushNodes[cur_x][cur_y][last_dir].pre_row;int pre_y = gPushNodes[cur_x][cur_y][last_dir].pre_col;PeopleMoveNode move_node(false, pre_x, pre_y, last_dir,cur_x ,cur_y);gPushNodestack.push(move_node);cur_x = pre_x;cur_y = pre_y;while (true){///printf("cur x = %d, cur y = %d\n", cur_x, cur_y);if (cur_x == gBoxPos[0] && cur_y == gBoxPos[1]){pre_x = cur_x - gMoveInc[last_dir][0];pre_y = cur_y - gMoveInc[last_dir][1];if (pre_x == gPeoplePos[0] && pre_y == gPeoplePos[1]){move_node.SetInfo(false, pre_x, pre_y, last_dir, cur_x, cur_y);gPushNodestack.push(move_node);}else{move_node.SetInfo(true, pre_x, pre_y, last_dir, cur_x, cur_y);gPushNodestack.push(move_node);move_node.SetInfo(false, gPeoplePos[0], gPeoplePos[1], last_dir, cur_x, cur_y);//人開始的起點, arrive_by_walk和last_dir不被使用,隨便設gPushNodestack.push(move_node);}break;}//說明發生了變向if (gPushNodes[cur_x][cur_y][last_dir].pre_dir){//發生轉彎,記錄下轉彎後,人應該在的位置pre_x = cur_x - gMoveInc[last_dir][0];pre_y = cur_y - gMoveInc[last_dir][1];move_node.SetInfo(true, pre_x, pre_y, last_dir, cur_x, cur_y);gPushNodestack.push(move_node);last_dir = gPushNodes[cur_x][cur_y][last_dir].pre_dir;pre_x = gPushNodes[cur_x][cur_y][last_dir].pre_row;pre_y = gPushNodes[cur_x][cur_y][last_dir].pre_col;move_node.SetInfo(false, pre_x, pre_y, last_dir, cur_x, cur_y);gPushNodestack.push(move_node);}int tmp = cur_x;cur_x = gPushNodes[cur_x][cur_y][last_dir].pre_row;cur_y = gPushNodes[tmp][cur_y][last_dir].pre_col;}}void MoveBox(){PeopleMoveNode move_node_from = gPushNodestack.top();gPushNodestack.pop();PeopleMoveNode move_node_to;while (!gPushNodestack.empty()){move_node_to = gPushNodestack.top();gPushNodestack.pop();if (move_node_to.arrive_by_walk){FindMinStepWay(move_node_from.row, move_node_from.col, move_node_to.row,move_node_to.col, move_node_to.box_x, move_node_to.box_y);}else{int cur_x = move_node_from.row, cur_y = move_node_from.col;while (cur_x != move_node_to.row || cur_y != move_node_to.col){cur_x = cur_x + gMoveInc[move_node_from.dir_to_next][0];cur_y = cur_y + gMoveInc[move_node_from.dir_to_next][1];printf("%c", gPushDirChar[move_node_from.dir_to_next]);}}move_node_from = move_node_to;}}struct PosDirPair{unsigned char row;unsigned char col;unsigned char dir_from_pre;void SetInfo(unsigned char r, unsigned char c, unsigned char dir){row = r;col = c;dir_from_pre = dir;}};void Solve(int cases){printf("Maze #%d\n", cases);memset(gPushNodes, 0, sizeof(gPushNodes));queue<PosDirPair>Q;PosDirPair pos_dir_pair;pos_dir_pair.row = gBoxPos[0];pos_dir_pair.col = gBoxPos[1];pos_dir_pair.dir_from_pre = 255;Q.push(pos_dir_pair);int people_x = gPeoplePos[0];int people_y = gPeoplePos[1];int box_x, box_y, dir_from_pre, box_next_x, box_next_y, people_next_x, people_next_y;while (!Q.empty()){pos_dir_pair = Q.front();Q.pop();box_x = pos_dir_pair.row;box_y = pos_dir_pair.col;dir_from_pre = pos_dir_pair.dir_from_pre;if (box_x == gTargetPos[0] && box_y == gTargetPos[1]){SetNodeStack();MoveBox();printf("\n\n");return;}if (dir_from_pre != 255){people_x = box_x - gMoveInc[dir_from_pre][0];people_y = box_y - gMoveInc[dir_from_pre][1];}else{people_x = gPeoplePos[0];people_y = gPeoplePos[1];}for (int i = 1; i <= 4; i++){//初始的 dir不合法,為 255box_next_x = box_x + gMoveInc[i][0];box_next_y = box_y + gMoveInc[i][1];//pre_row 不是0,說明 點box_next_x, box_next_y 已經從i方向被進入過if (gCastle[box_next_x][box_next_y] == ‘#‘ || gPushNodes[box_next_x][box_next_y][i].pre_row){continue;}people_next_x = box_x - gMoveInc[i][0];people_next_y = box_y - gMoveInc[i][1];if (! CanPushFromPos(people_next_x, people_next_y, people_x, people_y, box_x, box_y)){continue;}gPushNodes[box_next_x][box_next_y][i].pre_row = box_x;gPushNodes[box_next_x][box_next_y][i].pre_col = box_y;//記錄下來轉彎的點的轉彎之前的方向if (gPushNodes[box_x][box_y][i].pre_row == 0){gPushNodes[box_x][box_y][i].pre_dir = dir_from_pre;}pos_dir_pair.SetInfo(box_next_x, box_next_y, i);Q.push(pos_dir_pair);}}printf("Impossible.\n\n");}int main(){int cases = 1;while (true){scanf("%d %d", &gRow, &gCol);if (gRow == 0){break;}Input();Solve(cases ++);}return 0;}
poj_1475 BFS+BFS