UVA1601 The Morning after Halloween

版权声明:听说这里让写版权声明~~~ https://blog.csdn.net/m0_37691414/article/details/83714495

题意翻译

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;
}

代码中使用了编码和解码,为得就是节省空间。

猜你喜欢

转载自blog.csdn.net/m0_37691414/article/details/83714495