1601 - The Morning after Halloween

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

你可能感兴趣的:(ACM,uva,UVa1601)