PS:因为该题排版较麻烦,这里给出OJ网址:UVa 1601 - The Morning after Halloween
w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼)。要求把它们分别移动到对应的大写字母里。每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占用同一个位置,也不能在一步之内交换位置。
输入保证所有空格连通,所有障碍格也连通,且任何一个2*2子网格中至少有一个障碍格。输出最少的步数。输入保证有解。
RuJia的写法非常精巧,理解不易
// UVa1601 The Morning after Halloween
// Rujia Liu
// This code implements the simpliest yet efficient-enough algorithm I'm aware of
// Readers are encouraged to experiment on other algorithms (especially for better efficiency!)
#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
using namespace std;
const int maxs = 20;
const int maxn = 150; // 75% cells plus 2 fake nodes
const int dx[]={1,-1,0,0,0}; // 4 moves, plus "no move"
const int dy[]={0,0,1,-1,0};
inline int ID(int a, int b, int c) {
return (a<<16)|(b<<8)|c;
}
int s[3], t[3]; // starting/ending position of each ghost
int deg[maxn], G[maxn][5]; // target cells for each move (including "no move")
inline bool conflict(int a, int b, int a2, int b2) {
return a2 == b2 || (a2 == b && b2 == a);
}
int d[maxn][maxn][maxn]; // distance from starting state
int bfs() {
queue<int> q;
memset(d, -1, sizeof(d));
q.push(ID(s[0], s[1], s[2])); // starting node
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]; // solution found
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(b, c, b2, c2)) 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];
// 读入floor
for(int i = 0; i < h; i++)
fgets(maze[i], 20, stdin);
// extract empty cells
int cnt, x[maxn], y[maxn], id[maxs][maxs]; // cnt is the number of empty cells
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++;
}
// build a graph of empty cells
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];
// "Outermost cells of a map are walls" means we don't need to check out-of-bound
if(maze[nx][ny] != '#') G[i][deg[i]++] = id[nx][ny];
}
}
// add fakes nodes so that in each case we have 3 ghosts. this makes the code shorter
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;
}
自己写的表示超时了
//#define LOCAL
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
const int maxH = 16 + 5;
// 网格宽,长,ghost数量
int w, h, n;
// 初始状态
int start[maxH][maxH];
// 初始状态的a,b,c的位置
int ar = 0, ac = 0, br = 0, bc = 0, cr = 0, cc = 0;
// 目标状态
int goal[maxH][maxH];
// 目标状态的a,b,c的位置
int are = 0, ace = 0, bre = 0, bce = 0, cre = 0, cce = 0;
// 是否走廊
int isCorridor[maxH][maxH];
struct Node {
// 状态
int v[maxH][maxH];
// 当前的a,b,c的位置
int ar, ac, br, bc, cr, cc;
// 距离
int dist;
};
// 移动方位, 上下左右,不动
int dx[] = {-1, 1, 0, 0, 0};
int dy[] = { 0, 0, -1, 1, 0};
const int haseSize = 100003;
vector<int> head[haseSize];
void init_lookup_table2() {
for(int i = 0; i < haseSize; i++) {
head[i].clear();
}
}
int myhash(int sum) {
return sum % haseSize;
}
bool try_insert2(const Node &u) {
int sum = 0;
sum += (u.ac << 20);
sum += (u.ar << 16);
sum += (u.bc << 12);
sum += (u.br << 8);
sum += (u.cr << 4);
sum += (u.cc);
int h = myhash(sum);
// 开放定址法
for(int i = 0; i < head[h].size(); i++) {
if(head[h][i] == sum) {
return false;
}
}
head[h].push_back(sum);
return true;
}
set<long> vis;
// 初始化访问表
void init_lookup_table() {
vis.clear();
}
// 尝试插入
bool try_insert(const Node &u) {
long sum = 0;
sum += u.ac * 10000000000;
sum += u.ar * 100000000;
sum += u.bc * 1000000;
sum += u.br * 10000;
sum += u.cr * 100;
sum += u.cc;
if(vis.count(sum)) {
return false;
}
vis.insert(sum);
return 1;
}
void solve() {
// for(int i = 0; i < h; i++) {
// for(int j = 0; j < w; j++) {
// if(start[i][j] == 0) {
// cout << "#";
// } else if(start[i][j] == 1) {
// cout << " ";
// } else if(start[i][j] == 'a') {
// cout << "a";
// } else if(start[i][j] == 'b') {
// cout << "b";
// } else if(start[i][j] == 'c') {
// cout << "c";
// }
// }
// cout << endl;
// }
// cout << endl;
// for(int i = 0; i < h; i++) {
// for(int j = 0; j < w; j++) {
// if(isCorridor[i][j] == 0) {
// cout << "#";
// } else if(isCorridor[i][j] == 1) {
// cout << " ";
// }
// }
// cout << endl;
// }
// cout << "(" << ar << "," << ac << ") ("
// << br << "," << bc << ") ("
// << cr << "," << cc << ")" << endl;
// cout << "(" << are << "," << ace << ") ("
// << bre << "," << bce << ") ("
// << cre << "," << cce << ")" << endl;
// 初始化访问表
init_lookup_table2();
// 初始节点
Node sta;
memcpy(&sta.v, &start, sizeof(start));
sta.dist = 0;
sta.ar = ar;
sta.br = br;
sta.cr = cr;
sta.ac = ac;
sta.bc = bc;
sta.cc = cc;
queue<Node> q;
q.push(sta);
// BFS
while(!q.empty()) {
Node u = q.front();
q.pop();
// cout << "(" << u.ar << "," << u.ac << ") ("
// << u.br << "," << u.bc << ") ("
// << u.cr << "," << u.cc << ")" << endl;
// 找到终点状态,退出
if(u.ar == are && u.ac == ace && u.br == bre && u.bc == bce && u.cr == cre && u.cc == cce) {
cout << u.dist << endl;
return;
}
// 根据goast数目确定循环层数
if(n == 1) {
for(int i = 0; i < 5; i++) {
// 下一个位置
int nextAR = u.ar + dx[i];
int nextAC = u.ac + dy[i];
// 走廊
if(isCorridor[nextAR][nextAC]) {
Node u2;
memcpy(&u2, &u, sizeof(u));
// 距离加一
u2.dist++;
// 交换goast与空格位置
swap(u2.v[u2.ar][u2.ac],u2.v[nextAR][nextAC]);
u2.ar = nextAR;
u2.ac = nextAC;
if(try_insert2(u2)) {
q.push(u2);
}
}
}
} else if(n == 2) {
for(int i = 0; i < 5; i++) {
for(int j = 0; j < 5; j++) {
// 下一个位置
int nextAR = u.ar + dx[i];
int nextAC = u.ac + dy[i];
int nextBR = u.br + dx[j];
int nextBC = u.bc + dy[j];
// 走廊
if(isCorridor[nextAR][nextAC] && isCorridor[nextBR][nextBC]) {
// 不能在同一个位置,不能交换位置
if((nextAR == nextBR && nextAC == nextBC) || (nextAR == u.br && nextAC == u.bc && nextBR == u.ar && nextBC == u.ac)) {
continue;
} else {
Node u2;
memcpy(&u2, &u, sizeof(u));
// 距离加一
u2.dist++;
// 交换goast与空格位置
swap(u2.v[u2.ar][u2.ac],u2.v[nextAR][nextAC]);
swap(u2.v[u2.br][u2.bc],u2.v[nextBR][nextBC]);
u2.ar = nextAR;
u2.ac = nextAC;
u2.br = nextBR;
u2.bc = nextBC;
if(try_insert2(u2)) {
q.push(u2);
}
}
}
}
}
} else if(n == 3) {
for(int i = 0; i < 5; i++) {
for(int j = 0; j < 5; j++) {
for(int k = 0; k < 5; k++) {
// 下一个位置
int nextAR = u.ar + dx[i];
int nextAC = u.ac + dy[i];
int nextBR = u.br + dx[j];
int nextBC = u.bc + dy[j];
int nextCR = u.cr + dx[k];
int nextCC = u.cc + dy[k];
// 走廊
if(isCorridor[nextAR][nextAC] && isCorridor[nextBR][nextBC] && isCorridor[nextCR][nextCC]) {
// 不能在同一个位置,不能交换位置
if((nextAR == nextBR && nextAC == nextBC)
|| (nextAR == nextCR && nextAC == nextCC)
|| (nextBR == nextCR && nextBC == nextCC)
|| (nextAR == u.br && nextAC == u.bc && nextBR == u.ar && nextBC == u.ac)
|| (nextAR == u.cr && nextAC == u.cc && nextCR == u.ar && nextCC == u.ac)
|| (nextBR == u.cr && nextBC == u.cc && nextCR == u.br && nextCC == u.bc)) {
continue;
} else {
Node u2;
memcpy(&u2, &u, sizeof(u));
// 距离加一
u2.dist++;
// 交换goast与空格位置
swap(u2.v[u2.ar][u2.ac],u2.v[nextAR][nextAC]);
swap(u2.v[u2.br][u2.bc],u2.v[nextBR][nextBC]);
swap(u2.v[u2.cr][u2.cc],u2.v[nextCR][nextCC]);
u2.ar = nextAR;
u2.ac = nextAC;
u2.br = nextBR;
u2.bc = nextBC;
u2.cr = nextCR;
u2.cc = nextCC;
if(try_insert2(u2)) {
// cout << "(" << u.ar << "," << u.ac << ") ("
// << nextAR << "," << nextAC << ")\t("
// << u.br << "," << u.bc << ") ("
// << nextBR << "," << nextBC << ")\t("
// << u.cr << "," << u.cc << ") ("
// << nextCR << "," << nextCC << ")" << endl;
q.push(u2);
}
}
}
}
}
}
}
}
}
int main() {
#ifdef LOCAL
freopen("data.1601.in", "r", stdin);
freopen("data.1601.out", "w", stdout);
#endif // LOCAL
while(cin >> w >> h >> n) {
if(!w) {
break;
}
// 多少行
for(int i = 0; i < h; i++) {
}
memset(start, 0, sizeof(start));
memset(goal, 0, sizeof(goal));
memset(isCorridor, 0, sizeof(isCorridor));
string s;
getline(cin, s);
for(int i = 0; i < h; i++) {
getline(cin, s);
for(int j = 0; j < s.length(); j++) {
switch(s[j]) {
// 走廊
case ' ' : {
start[i][j] = 1;
goal[i][j] = 1;
isCorridor[i][j] = 1;
break;
}
// 目标位置
case 'A' : {
goal[i][j] = 'a';
start[i][j] = 1;
isCorridor[i][j] = 1;
are = i;
ace = j;
break;
}
case 'B' : {
goal[i][j] = 'b';
start[i][j] = 1;
isCorridor[i][j] = 1;
bre = i;
bce = j;
break;
}
case 'C' : {
goal[i][j] = 'c';
start[i][j] = 1;
isCorridor[i][j] = 1;
cre = i;
cce = j;
break;
}
// 初始位置
case 'a' : {
start[i][j] = 'a';
isCorridor[i][j] = 1;
ar = i;
ac = j;
break;
}
case 'b' : {
start[i][j] = 'b';
isCorridor[i][j] = 1;
br = i;
bc = j;
break;
}
case 'c' : {
start[i][j] = 'c';
isCorridor[i][j] = 1;
cr = i;
cc = j;
break;
}
}
}
}
solve();
}
return 0;
}