【题目链接】点击打开链接

【题意】w*h(w,h <= 16)网格中有n(n<=3)个小写字母(代表鬼),要求把它们分别移动到对应的大写字母里。每一步可以有多个鬼移动(均为往上下左右4个方向之1移动),但每一步结束之后,任何两个鬼不能占用同一个位置,也不能在一步之内交换位置。

【解题方法】方法是把所有可以移动的格子找出来建立一张图,就是把障碍物给删除,统计每个可以空格或者有鬼的格子可以移动到哪些格子,这样在判断的时候就节省了许多时间。然后bfs找最短路。具体可以看紫书206页,书上还提到了可以用双向BFS的方法,现在不会。


【AC代码】


//
//Created by BLUEBUFF 2016/1/9
//Copyright (c) 2016 BLUEBUFF.All Rights Reserved
//

#pragma comment(linker,"/STACK:102400000,102400000")
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdio>
#include <time.h>
#include <cstdlib>
#include <cstring>
#include <complex>
#include <sstream> //isstringstream
#include <iostream>
#include <algorithm>
using namespace std;
//using namespace __gnu_pbds;
typedef long long LL;
typedef pair<int, LL> pp;
#define REP1(i, a, b) for(int i = a; i < b; i++)
#define REP2(i, a, b) for(int i = a; i <= b; i++)
#define REP3(i, a, b) for(int i = a; i >= b; i--)
#define CLR(a, b)     memset(a, b, sizeof(a))
#define MP(x, y)      make_pair(x,y)
template <class T1, class T2>inline void getmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void getmin(T1 &a, T2 b) { if (b<a)a = b; }
const int maxn = 20;
const int maxm = 300;
const int maxs = 10;
const int maxp = 1e3 + 10;
const int INF  = 1e9;
const int UNF  = -1e9;
const int mod  = 1e9 + 7;
const double PI = acos(-1);
//head

int getnum(int a, int b, int c){ //编码
    return (a << 16) | (b << 8) | c;
}

int ok(int a, int b, int a1, int b1){ //两种非法的情况
    return ((a1 == b1) || (a1 == b && b1 == a));
}

int dir[5][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}, {0, 0}};
int id[maxn][maxn], G[maxm][5]; //由于相邻节点最多5个,包含自身
int st[3], en[3]; //st存放起始位置,en存放结束位置
int edge[maxm], d[maxm][maxm][maxm];//edge数组存放第i个结点具有的相邻结点数目
int w, h, n, cnt;
char M[maxn][maxn]; //原地图
void bfs()
{
    queue <int> q;
    memset(d, -1, sizeof(d));
    q.push(getnum(st[0], st[1], st[2]));
    d[st[0]][st[1]][st[2]] = 0;
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        int a = (u >> 16) & 255, b = (u >> 8) & 255, c = u & 255; //解码各个鬼的位置
        if(a == en[0] && b == en[1] && c == en[2]) return; //找个目标状态
        for(int i = 1; i <= edge[a]; i++){
            int a1 = G[a][i];
            for(int j = 1; j <= edge[b]; j++){
                int b1 = G[b][j];
                if(ok(a, b, a1, b1)) continue;
                for(int k = 1; k <= edge[c]; k++){
                    int c1 = G[c][k];
                    if(ok(a, c, a1, c1)) continue;
                    if(ok(b, c, b1, c1)) continue;
                    if(d[a1][b1][c1] == -1){
                        d[a1][b1][c1] = d[a][b][c] + 1;
                        q.push(getnum(a1, b1, c1));
                    }
                }
            }
        }
    }
}
int main()
{
    while(scanf("%d%d%d", &w, &h, &n) != EOF && n)
    {
        char c = getchar();
        while(c != '\n') c = getchar();
        REP2(i, 1, h){
            fgets(M[i] + 1, 20, stdin);
        }
        cnt = 0;
        int x[maxm], y[maxm];
        REP2(i, 1, h){
            REP2(j, 1, w){
                if(M[i][j] != '#'){
                    id[i][j] = ++cnt;//利用矩阵存稀疏图,cnt是每个结点的序号,也代表了最终的结点数
                    x[cnt] = i;
                    y[cnt] = j;
                    if('a' <= M[i][j] && M[i][j] <= 'c'){
                        st[M[i][j] - 'a'] = cnt; //找每一个鬼
                    }
                    if('A' <= M[i][j] && M[i][j] <= 'C'){
                        en[M[i][j] - 'A'] = cnt; //找目标位置
                    }
                }
            }
        }
        REP2(i, 1, cnt){
            edge[i] = 0;
            REP1(j, 0, 5){
                int dx = x[i] + dir[j][0];
                int dy = y[i] + dir[j][1];
                if(M[dx][dy] != '#'){
                    G[i][++edge[i]] = id[dx][dy];//寻找每个结点的相邻结点,包括自己
                }
            }
        }
        if(n <= 2){
            edge[++cnt] = 1;
            G[cnt][1] = cnt;
            st[2] = en[2] = cnt;
        }
        if(n <= 1){
            edge[++cnt] = 1;
            G[cnt][1] = cnt;
            st[1] = en[1] = cnt;
        }
        bfs();
        printf("%d\n", d[en[0]][en[1]][en[2]]);
    }
    return 0;
}