題目連結:
UVa : http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=1215
zoj : http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1237
類型: 隱式圖搜尋+噁心類比+雜湊判重+bfs+dfs
原題:
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
題目大意:
Tomy很喜歡一款遊戲叫做粉絲和寶石(Fans and Gems,第一個單詞不知道該怎麼翻譯), 玩家可以讓粉絲去收集寶石。Tomy希望可以用最少的步數把所有的寶石都收集起來。 遊戲的玩法是這樣的:
寶石有三種:紅,綠,藍, 分別用1,2,3來代替,地圖的四周有牆,地圖中還可能有飛行器,用@代替。 粉絲是虛擬,看不到的,它們在地圖的每一個角落,無處不在。 你每一次可以選擇一個方向(上,下,左,右), 然後這些粉絲就會往這個方向推地圖上的所有寶石和飛行器(其實就是選擇一個方向,然後地圖中所有的寶石和飛行器都會同時往這個方向以相同的速度移動)。當寶石和飛行器碰到牆就會停止下來。 或者遇到了一個已經停止下來的寶石和飛行器,也會停止下來。 在移動的過程中,一旦發現有相同的寶石是相鄰的,那麼相鄰的那些相同寶石都會消失。
分析與總結:
題目倒是挺好玩的,但是……這一題涉及到的類比過程比上一題的噁心程度以指數級增加……
需要類比整個地圖中寶石和飛行器的移動過程,然後還要消除掉相鄰的相同的寶石。
把過程類比好,也就向AC邁出了一大步。
然後就是像做一般的隱式圖搜尋+雜湊判重一樣。
還有一個需要注意的地方,如果有多種方案,那麼輸出字典序最小的那個, 這樣只需要在搜尋時,方向按照字典序D,L,R, U的順序搜尋下去即可。
WA了一次,原因:
1. 大於18步的就不再考慮。
2. 每一個輸入之後會多出一行無用的, 而且不一定是空行(這個還是看了discuss給的輸入輸出才知道的),要把這一行讀掉。
/* * UVa 10274 - Fans and Gems * 隱式圖搜尋 + BFS + 類比 + 雜湊判重 * Time: 0.376s (UVa) 590ms(zoj) * Author: D_Double */#include<iostream> #include<cstring> #include<cstdio> using namespace std; typedef char State[13][21]; State map; int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; char dir_name[] = {'D', 'L', 'R', 'U'}; int N,M; inline void swap(char &ch1, char &ch2){ char t=ch1; ch1=ch2; ch2=t; } inline bool is_gem_fly(char ch){ if(ch=='1'||ch=='2'||ch=='3'||ch=='@') return true; return false; } // 類比個方向移動過程,注意按字典順序:D,L,R,U // 本文URL:http://www.bianceng.cn/Programming/sjjg/201410/45633.htminline bool move(State &s, int dir){ bool flag=false; if(dir==0){ // 下 for(int i=1; i<M-1; ++i){ for(int j=N-1; j>0; --j)if(s[j][i]==' '){ int k = j-1; while(s[k][i]==' ') --k; if(is_gem_fly(s[k][i])){ swap(s[j][i], s[k][i]); flag=true; } else j=k; } } } else if(dir==1){ // 左 for(int i=1; i<N-1; ++i){ for(int j=1; j<M-1; ++j)if(s[i][j]==' '){ int k = j+1; while(s[i][k]==' ') ++k; if(is_gem_fly(s[i][k])){ swap(s[i][j], s[i][k]); flag=true; } else j=k; } } } else if(dir==2){ // 右 for(int i=1; i<N-1; ++i){ for(int j=M-1; j>1; --j)if(s[i][j]==' '){ int k = j-1; while(s[i][k]==' ') --k; if(is_gem_fly(s[i][k])){ swap(s[i][j], s[i][k]); flag=true; } else j=k; } } } else if(dir==3){ // 上 for(int i=1; i<M-1; ++i){ for(int j=1; j<N-1; ++j)if(s[j][i]==' '){ int k = j+1; while(s[k][i]==' ')++k; if(is_gem_fly(s[k][i])){ swap(s[j][i], s[k][i]); flag=true; } else j=k; } } } if(flag)return true; return false; } void dfs(State &s, char ch, int x, int y){ // 消除相同的寶石 s[x][y] = ' '; for(int i=0; i<4; ++i){ int dx=x+dir[i][0], dy=y+dir[i][1]; if(s[dx][dy]==ch) dfs(s, ch, dx, dy); } } inline bool remove(State &s){ bool ok=false; for(int i=1; i<N-1; ++i){ for(int j=1; j<M-1; ++j)if(is_gem_fly(s[i][j]) && s[i][j]!='@'){ bool flag=false; for(int k=0; k<4; ++k){ int dx=i+dir[k][0], dy=j+dir[k][1]; if(dx>=0 && dx<N && dy>=0 && dy<M && s[i][j]==s[dx][dy]){ flag=true; break; } } if(flag){ ok = true; dfs(s,s[i][j],i,j); } } } if(ok) return true; return false; } inline void run(State &s, int dir){ // 往哪個方向的步驟 while(move(s, dir)){ remove(s); } } const int MAXN = 100000; const int HashSize = 1000003; State que[MAXN]; int head[HashSize], next[MAXN], father[MAXN], step[MAXN], ans; char path[MAXN]; inline void init_lookup_table(){ ans = -1; father[0]=0; step[0] = 0; memset(head, 0, sizeof(head)); memset(que, 0, sizeof(que)); } inline int hash(State &s){ int seed=131, v=0; for(int i=0; i<N; ++i){ for(int j=0; j<M; ++j) v = (v*seed+s[i][j]) & 0x7FFFFFFF; } return v%HashSize; } bool try_to_insert(int s){ int h = hash(que[s]); int u = head[h]; while(u){ if(memcmp(que[u], que[s], sizeof(que[s]))==0)return false; u = next[u]; } next[s] = head[h]; head[h] = s; return true; } inline bool is_ok(State &s){ for(int i=1; i<N-1; ++i){ for(int j=1; j<M-1; ++j) if(s[i][j]=='1'||s[i][j]=='2'||s[i][j]=='3')return false; } return true; } bool bfs(){ init_lookup_table(); int front=0, rear=1; memcpy(que[0], map, sizeof(map)); try_to_insert(0); while(front < rear){ State &s = que[front]; if(step[front] > 18){ ++front; continue; } if(is_ok(s)){ ans = front; return true; } for(int i=0; i<4; ++i){ State &t = que[rear]; memcpy(t, s, sizeof(s)); run(t, i); if(try_to_insert(rear)){ step[rear] = step[front]+1; father[rear] = front; path[rear] = dir_name[i]; ++rear; } } ++front; } return false; } void print_path(int cur){ if(cur){ print_path(father[cur]); printf("%c", path[cur]); } } int main(){ int T; char str[1000]; scanf("%d%*c",&T); while(T--){ scanf("%d%d%*c",&N,&M); for(int i=0; i<N; ++i) gets(map[i]); gets(str); if(!bfs()) printf("-1\n"); else{ print_path(ans); printf("\n"); } } return 0; }