1. 程式人生 > >UVA1601 The Morning after Halloween

UVA1601 The Morning after Halloween

題意翻譯

w h (w, h <= 16)的網格有 n ( n <= 3) 個小寫字母(代表鬼)其餘的是‘#’(代表障礙格) 或 ‘ ’(代表空格。 要求把他們移動到對應的大寫字母裡。每步可以有多少個鬼同時移動(均為上下左右4個移動方向之一), 但每步移動兩個鬼不能佔用同一個位置, 也不能在一步之內交換位置。輸入保證空格聯通,障礙聯通,且在2 2子網格中至少有一個障礙格,並且最外面一層是障礙格。輸入保證有解。

題解

很多問題都可以歸結為圖的遍歷,但是這些問題中的圖卻不是事先給定、從程式讀入,而是由程式直接生成的,稱為隱式圖。

本題和八皇后問題有明顯區別,八皇后問題屬於回溯法

。(一般要找到一個(或者所有)滿足約束的解(或者某種意義最優解)),而本題明顯是一個狀態空間搜尋一般是要找到一個初始狀態到終止狀態的路徑。

本題可以提取出所有空格和字母建立一張隱式圖。

#include<cstdio>
#include<cstring>
#include<queue>
#include<cctype>
using namespace std;
const int maxn = 256;
const int maxs = 20;
int G[maxn][5], d[maxn][maxn][maxn], s[3], t[3], deg[maxn];
int dx[] = {-1, 1, 0, 0, 0};
int dy[] = {0, 0, -1, 1, 0};
inline int ID(int a, int b, int c){
	return (a<<16)|(b<<8)|c; //編碼
}
inline bool conflict(int a, int b, int a2, int b2){
	return (a2 == b2) || (a2 == b && b2 == a);
}
int bfs() {
	queue<int> q;
	memset(d, -1, sizeof(d));
	q.push(ID(s[0], s[1], s[2]));
	d[s[0]][s[1]][s[2]] = 0;
	while(!q.empty()){
		int u = q.front(); q.pop();
		int a = (u >> 16) & 0xff, b = (u >> 8) & 0xff, c = u &0xff; //解碼 (巧妙不)
		if(a == t[0] && b == t[1] && c == t[2]) return d[a][b][c];
		for(int i = 0; i < deg[a]; ++i){
			int a2 = G[a][i];
			for(int j = 0; j < deg[b]; ++j){
				int b2 = G[b][j];
				if(conflict(a, b, a2, b2)) continue;
				for(int k = 0; k < deg[c]; ++k){
					int c2 = G[c][k];
					if(conflict(a, c, a2, c2)) continue;
					if(conflict(c, b, c2, b2)) continue;
					if(d[a2][b2][c2] != -1) continue;
					d[a2][b2][c2] = d[a][b][c] + 1;
					q.push(ID(a2, b2, c2));
				}
			}
		}
	}
	return -1;
}
int main() {
  int w, h, n; 
  while(scanf("%d%d%d\n", &w, &h, &n) == 3 && n) {
    char maze[20][20];
    for(int i = 0; i < h; i++)
      fgets(maze[i], 20, stdin);

    int cnt, x[maxn], y[maxn], id[maxs][maxs];
    cnt = 0;
    for(int i = 0; i < h; i++)
      for(int j = 0; j < w; j++)
        if(maze[i][j] != '#') {
          x[cnt] = i; y[cnt] = j; id[i][j] = cnt;
          if(islower(maze[i][j])) s[maze[i][j] - 'a'] = cnt;
          else if(isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;
          cnt++;
        }

    for(int i = 0; i < cnt; i++) {
      deg[i] = 0;
      for(int dir = 0; dir < 5; dir++) {
        int nx = x[i]+dx[dir], ny = y[i]+dy[dir];
        if(maze[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny];
      }
    }

    if(n <= 2) { deg[cnt] = 1; G[cnt][0] = cnt; s[2] = t[2] = cnt++; }
    if(n <= 1) { deg[cnt] = 1; G[cnt][0] = cnt; s[1] = t[1] = cnt++; }

    printf("%d\n", bfs());
  }
  return 0;
}

程式碼中使用了編碼和解碼,為得就是節省空間。