UVA - 1601 The Morning after Halloween : 状态空间搜索 坐标编号 双射 16进制编码

本文介绍了一种解决迷宫中幽灵定位问题的算法。该算法通过状态空间搜索,利用编码技巧降低复杂度,实现了将迷宫中的幽灵从初始位置移动到目标位置所需的最小步数计算。

题目点此跳转

You are working for an amusement park as an operator of an obakeyashiki, or a haunted house, in which guests walk through narrow and dark corridors. The house is proud of their lively ghosts, which are actually robots remotely controlled by the operator, hiding here and there in the corridors. One morning, you found that the ghosts are not in the positions where they are supposed to be. Ah, yesterday was Halloween. Believe or not, paranormal spirits have moved them around the corridors in the night. You have to move them into their right positions before guests come. Your manager is eager to know how long it takes to restore the ghosts.
 In this problem, you are asked to write a program that, given a floor map of a house, finds the smallest number of steps to move all ghosts to the positions where they are supposed to be. A floor consists of a matrix of square cells. A cell is either a wall cell where ghosts cannot move into or a corridor cell where they can.
 At each step, you can move any number of ghosts simultaneously. Every ghost can either stay in the current cell, or move to one of the corridor cells in its 4-neighborhood (i.e. immediately left, right, up or down), if the ghosts satisfy the following conditions:

  • No more than one ghost occupies one position at the end of the step.
  • No pair of ghosts exchange their positions one another in the step.

 For example, suppose ghosts are located as shown in the following (partial) map, where a sharp sign (‘#’) represents a wall cell and ‘a’, ‘b’, and ‘c’ ghosts.

####
ab #
#c##
####

 The following four maps show the only possible positions of the ghosts after one step.
####  ####  ####  ####
 ab#  a b#   acb#  ab #
#c##  #c##   # ##  #c##
####  ####  ####  ####

Input

The input consists of at most 10 datasets, each of which represents a floor map of a house. The format of a dataset is as follows.
w h n
c11 c12 … c1w
c21 c22 … c2w
. . .   .
. .  .  .
. .   . .
ch1 ch2 … chw
w, h and n in the first line are integers, separated by a space. w and h are the floor width and height of the house, respectively. n is the number of ghosts. They satisfy the following constraints.

4w16,4h16,1n3

 Subsequent h lines of w characters are the floor map. Each of cij is either:

  • a ‘#’ representing a wall cell,
  • a lowercase letter representing a corridor cell which is the initial position of a ghost,
  • an uppercase letter representing a corridor cell which is the position where the ghost corresponding to its lowercase letter is supposed to be, or
  • a space representing a corridor cell that is none of the above.

 In each map, each of the first n letters from a and the first n letters from A appears once and only once. Outermost cells of a map are walls; i.e. all characters of the first and last lines are sharps; and the first and last characters on each line are also sharps. All corridor cells in a map are connected; i.e. given a corridor cell, you can reach any other corridor cell by following corridor cells in the 4-neighborhoods. Similarly, all wall cells are connected. Any 2 × 2 area on any map has at least one sharp. You can assume that every map has a sequence of moves of ghosts that restores all ghosts to the positions where they are supposed to be.
 The last dataset is followed by a line containing three zeros separated by a space.

Output

 For each dataset in the input, one line containing the smallest number of steps to restore ghosts into the positions where they are supposed to be should be output. An output line should not contain extra characters such as spaces.

Sample Input

5 5 2
#####
#A#B#
# #
#b#a#
#####
16 4 3
################
## ########## ##
# ABCcba #
################
16 16 3
################
### ## # ##
## # ## # c#
# ## ########b#
# ## # # # #
# # ## # # ##
## a# # # # #
### ## #### ## #
## # # # #
# ##### # ## ##
#### #B# # #
## C# # ###
# # # ####### #
# ###### A## #
# # ##
################
0 0 0

Sample Output

7
36
77

思路

 题目意思是w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼)。要求把它们分别移动到对应的大写字母里。每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占用同一个位置,也不能在一步之内交换位置。
 输入保证所有空格连通,所有障碍格也连通,且任何一个2*2子网格中至少有一个障碍格。输出最少的步数。输入保证有解。

 状态空间搜索题,假设有3个小写字母(1个和2个的特殊处理一下),这个时候就有6个坐标,因为地图的长宽最大都是16,所以每个坐标对应一个16进制位, 而 166=224<2e7 , 貌似数组可以开,所以只要拿这6个数做一个编码解码即可:
 编码: 将16个数依次放在每一个16进制位
 译码: 将编码的每一个16进制位取出来
 使用 x & 0xf可去掉一高位; 使用x >> 4可去掉一低位

  然后就是每一个坐标都有两个值(x和y), 我们不妨来个映射,给每一个空白的地方编个号.保证可以根据编号找到某个坐标的x和y, 同时可以根据x,y找到那个编号.代码如下:

//生成坐标 (i, j) 的编号 pid[i][j];
//可通过px[id]和py[id]找到编号为id的(x, y)坐标;
void getPos(int i, int j) {
    px[++tot] = i, py[tot] = j, pid[i][j] = tot;
}

 当我们尝试这样做的时候,我们会发现这会带来一个意外的惊喜! 当我们要移动小写字母时,我们只能将它们移动到空格的地方,那么我们可以预处理出所有的空白坐标的邻居坐标, 这样在bfs的过程中的状态就比较容易转移了.
 这样虽然也能过,但是还可以用逐层的双向bfs优化.

代码

#include <algorithm>
#include <iostream>
#include <sstream>
#include <utility>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdio>
#include <cmath>
#define met(a,b) memset(a, b, sizeof(a));
#define IN freopen("in.txt", "r", stdin);
#define OT freopen("ot.txt", "w", stdout);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int maxn = 1e1 + 10;
const int INF = 0x7fffffff;
const int dir[5][2] = {0,0,-1,0,1,0,0,-1,0,1};

string s[maxn];
int w, h, n;
int px[maxn*maxn], py[maxn*maxn], pid[maxn][maxn], tot;
int neb[maxn*maxn][6], nid[maxn*maxn];
int st[3], ed[3];
bool vis[maxn*maxn*maxn*maxn*maxn*maxn];

void init() { tot = 0; met(neb, 0); met(nid, 0); met(vis, 0); }
void getPos(int i, int j) {
    px[++tot] = i, py[tot] = j, pid[i][j] = tot;
}

int dis[maxn*maxn*maxn*maxn*maxn*maxn];

int code(int a, int b, int c) {
    if(n <= 2) c = 0;
    if(n <= 1) b = 0;
    int ta = (px[a]<<4)+py[a], tb = (px[b]<<4)+py[b], tc = (px[c]<<4)+py[c];
    return ((ta<<16)|(tb<<8)|tc);
}
void decode(int t, int& a, int& b, int& c) {
    int ta = t >> 16;
    int tb = (t & 0xffff)>>8;
    int tc = (t & 0xff);
    int ax = ta >> 4, ay = ta & 0xf;
    int bx = tb >> 4, by = tb & 0xf;
    int cx = tc >> 4, cy = tc & 0xf;
    a = pid[ax][ay];
    b = pid[bx][by];
    c = pid[cx][cy];
    if(n <= 2) c = 0;
    if(n <= 1) b = 0;
}

void bfs() {
    queue<int> q;
    int c = code(st[0],st[1],st[2]);
    q.push(c); dis[c] = 0; vis[c] = 1;

    while(!q.empty()) {
        int t = q.front(); q.pop();
        int a, b, c; decode(t, a, b, c);
        //cout <<"abc"<<a << " " << b <<" " << c<<endl;
        if(a == ed[0] && b==ed[1]&&c == ed[2]) {
            cout << dis[t] <<endl; return;
        }
        int ta, tb, tc;
        for(int i = 0; i < nid[a]; ++i) {
            ta = neb[a][i];
            for(int j = 0; j < nid[b]; ++j) {
                tb = neb[b][j];
                for(int k = 0; k <nid[c]; ++k) {
                    tc = neb[c][k];
                    if(ta == tb || ta == tc || (n >1 && tb == tc)) continue;
                    if(ta == b && tb == a) continue;
                    if(ta == c && tc == a) continue;
                    if(n >1 && tb == c && tc == b) continue;
                    int tmp = code(ta, tb, tc);
                    if(!vis[tmp]) {
                        vis[tmp] = 1; q.push(tmp);
                        dis[tmp] = dis[t] + 1;
                    }
                }

            }

        }

    }
}

int main() {
    #ifdef _LOCAL
    IN; //OT;
    #endif // _LOCAL

    while(scanf("%d%d%d", &h, &w, &n) == 3 && n) {
        getline(cin, s[0]);
        for(int i = 0; i < w; ++i) getline(cin, s[i]);
        init();
        for(int i = 0; i < w; ++i)
        for(int j = 0; j < h; ++j) {
            if(s[i][j] != '#') getPos(i, j);
            if(s[i][j] >= 'a' && s[i][j] <= 'z') st[s[i][j]-'a'] = tot;
            else if(s[i][j] >= 'A' && s[i][j] <= 'Z') ed[s[i][j]-'A'] = tot;
        }
        for(int i = 1; i <= tot; ++i) {
            for(int j = 0; j < 5; ++j) {
                int tx = px[i] + dir[j][0];
                int ty = py[i] + dir[j][1];
                if(tx >= 0 && tx < w && ty >= 0 && ty < h && s[tx][ty] != '#') {
                    neb[i][nid[i]++] = pid[tx][ty];
                }
            }
        }
        if(n <= 2) { st[2] = ed[2] = 0; nid[0] = 1; neb[0][0] = 0; }
        if(n <= 1) { st[1] = ed[1] = 0; }
        bfs();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值