Fans and Gems
Input: Standard Input
Output: Standard Output
Tomy's fond of a game called 'Fans and Gems' (also known as Gravnic). In the game, he can use fans to collect gems, but he's satisfied with his play only if all the gems are collected with minimal number of steps. The game is played as following:
There are three kinds of gems, one colored red, one colored green and one colored blue. There are walls in the space, as you see. There are also virtual fans everywhere in the game, but you cannot see them. What you can do each time is to select a DIRECTION to which the fans should blow. There are only four directions possible: UP, DOWN, LEFT and RIGHT. Then, the fans will work, pushing all the gems to fly to the selected direction at the same speed, until they cannot move further(blocked by the wall, other gems or a flyer). Then, if there are some gems touching some same-colored gem(touching means adjacent in one of the four directions), they disappear simultaneously. Note that the fans are still working, so some gems may go on moving in that direction after some gems disappeared. The fans stop working when all the gems cannot move any more, and none of them should disappear. There may be some flyers that can be moved by the fans, but they can NEVER disappear.
You are to write a program that finds the minimal number of operations to make all the gems disappear.
Input
The input file begins with an integer T, indicating the number of test cases. (1<=T<=50) Each test case begins with two integers N, M, indicating the height and width of the map. (1<=N<=12,1<=M<=20) In the following N lines, each line contains M characters describing the map. There is one line after each map, ignore them. Spaces denotes empty square, '#' denotes a wall, '1' denotes a red gem, '2' denotes a green gem, '3' denotes a blue gem, and '@' denotes a flyer. It's guaranteed that the four sides of the map are all walls. There is at least one gem in the map, and no two same-colored gems will touch each other at the beginning of the game.
Output
You should print a single line for each case. If there is a solution, write the shortest operation sequence in a string. The ith character must be from the set {'U','D','L','R'}, describing ith operation. The four characters represent UP, DOWM, LEFT, RIGHT respectively. If there are more than one solution, choose the lexicographical smallest one, if there are no solution, output -1 on the line. When a solution exists, you need only 18 or fewer steps to carry it out.
Sample Input
2
9 8
########
## 1##
## 2 #
# 1 ##
##2 ##
# 1@##
### ##
#### ###
########
7 8
########
#212121#
# #
# # # ##
# # #
# ##
########
Sample Output
LURD
DL
___________________________________________________________________________________
题目大意:在一个地图上有若干个宝石,宝石分三类, 相同类的宝石相邻之后可以收取,‘@’代表飞行器(会跟着移动,但是不会消失),地图上每个格子有隐藏的fans,风扇,玩家可以通过操作这些风扇来移动宝石和飞行器,每次只能朝同一个方向移动,直到收集齐所有的宝石,输出字典序最小的路径,无法完成游戏输出-1.
解题思路:首先模拟出宝石移动的函数,以及宝石消除的函数,剩下的全是隐式图搜索的过程了。
#include <stdio.h> #include <string.h> const int MAXN = 100005; const int R = 15; const int C = 22; const int dir[4][2] = {{1, 0}, {0, -1}, {0, 1}, {-1, 0}}; const char sign[5] = "DLRU"; struct Map { char state[R][C]; char order[C]; int cnt; }aid, begin; int sumPace, sumGem; int r, c; bool isGem(char c) { if (c > '0' && c < '4' || c == '@') return true; return false; } void flyGem(Map now, int& x, int& y, int d) { do { x += dir[d][0]; y += dir[d][1]; }while (!isGem(now.state[x][y]) && now.state[x][y] != '#'); x -= dir[d][0]; y -= dir[d][1]; } void moveGem(Map& now, int d) { int x, y; if (d == 0) { for (int i = r - 1; i >= 0; i--) { for (int j = 0; j < c; j++) { if (isGem(now.state[i][j])) { x = i, y = j; flyGem(now, x, y, d); now.state[x][y] = now.state[i][j]; if (x != i || y != j) now.state[i][j] = ' '; } } } } else { for (int i = 0; i < r; i++) { if (d == 1) { for (int j = 0; j < c; j++) { if (isGem(now.state[i][j])) { x = i, y = j; flyGem(now, x, y, d); now.state[x][y] = now.state[i][j]; if (x != i || y != j) now.state[i][j] = ' '; } } } else { for (int j = c - 1; j >= 0; j--) { if (isGem(now.state[i][j])) { x = i, y = j; flyGem(now, x, y, d); now.state[x][y] = now.state[i][j]; if (x != i || y != j) now.state[i][j] = ' '; } } } } } } /////////////////////////////////////////////////////////// Map que[MAXN]; int head[MAXN], next[MAXN], dist[MAXN]; void inInit() { memset(head, 0, sizeof(head)); memset(que, 0, sizeof(que)); memcpy(que[1].state, begin.state, sizeof(begin.state)); dist[1] = 0; } inline int hash(Map &cur){ int seed = 131, v = 0; for(int i = 0; i < r; ++i){ for(int j = 0; j < c; ++j) v = (v * seed + cur.state[i][j]) & 0x7FFFFFFF; } return v % MAXN; } bool tryInsert(int cur) { int h = hash(que[cur]); int u = head[h]; while (u) { if (memcmp(que[cur].state, que[u].state, sizeof(que[u].state)) == 0) return false; u = next[u]; } next[cur] = head[h]; head[h] = cur; return true; } int dfs(Map& now, int x, int y, int ch) { int sum = 0; for (int i = 0; i < 4; i++) if (ch == now.state[x + dir[i][0]][y + dir[i][1]]) { now.state[x + dir[i][0]][y + dir[i][1]] = ' '; sum += dfs(now, x + dir[i][0], y + dir[i][1], ch); } return sum + 1; } int delGem(Map& now) { int sum = 0; char ch; for (int x = 0; x < r; x++) { for (int y = 0; y < c; y++) { int flag = 0; for (int i = 0; i < 4; i++) { if (now.state[x][y] == now.state[x + dir[i][0]][y + dir[i][1]] && isGem(now.state[x][y]) && now.state[x][y] != '@') { flag = 1; ch = now.state[x][y]; now.state[x][y] = ' '; break; } } if (flag) sum += dfs(now, x, y, ch); } } return sum; } bool bfs() { int front = 1, rear = 2, k; inInit(); while (front < rear) { Map& now = que[front]; if (dist[front] > 18) return false; if (now.cnt == sumGem) { aid = que[front]; sumPace = dist[front]; return true; } for (int i = 0; i < 4; i++) { Map& net = que[rear]; net = now; net.order[dist[front]] = sign[i]; while (1) { moveGem(net, i); k = delGem(net); if (k == 0) break; else net.cnt += k; } if (tryInsert(rear)) { dist[rear] = dist[front] + 1; rear++; } } front++; } return false; } int main() { int cas; char str[1000]; scanf("%d", &cas); while (cas--) { // Init; sumPace = sumGem = 0; // Read; scanf("%d%d%*c", &r, &c); for (int i = 0; i < r; i++) { gets(begin.state[i]); int len = strlen(begin.state[i]); for (int j = 0; j < len; j++) if (begin.state[i][j] > '0' && begin.state[i][j] < '4') sumGem++; } gets(str); if (bfs()) { aid.order[sumPace] = '\0'; puts(aid.order); } else printf("-1\n"); } return 0; }