1601:The Morning after Halloween(经典)

The Morning after Halloween

直接BFS会超时,题目中提示过墙壁很多,那么可以将所有的空格提取出来做张图,然后记录每个空格周围的邻居,这样就不用每次都判断能不能走了。优化的话可以使用双向BFS,从每次正向(从开始位置搜索)搜索一次,反向(从目标位置搜索)搜索一次,如果有相同状态则找到最短路径。

BFS(800ms):

#include<bits/stdc++.h>
using namespace std;
const int maxl = 20;
const int maxn = 150;
int w, h, n;
int deg[maxn];
int G[maxn][5];
int s[3], t[3];
int d[maxn][maxn][maxn];
int x[maxn], y[maxn], id[maxl][maxl];

char maze[maxl][maxl];

int dx[] = {0, 0, 0, -1, 1};
int dy[] = {0, 1, -1, 0, 0};

int ID(int a, int b, int c){
    return (a<<16) | (b<<8) | c;
}

bool conflict(int a, int b, int a2, int b2){
    return a2 == b2 || (a2 == b && b2 == a);
}

int BFS(){
    memset(d, -1, sizeof(d));
    queue<int>q;
    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(b, c, b2, c2)) continue;
                    if(d[a2][b2][c2] != -1) continue;
                    q.push(ID(a2, b2, c2));
                    d[a2][b2][c2] = d[a][b][c] + 1;
                }
            }
        }
    }
    return -1;
}

int main()
{
    // freopen("data.in","r",stdin);
    // freopen("data.out","w",stdout);
    while(scanf("%d%d%d", &w, &h, &n) == 3 && w){
        getchar();
        for(int i = 0; i < h; i++)
            fgets(maze[i], maxl, stdin);

        int 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;
                    if(isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;
                    cnt++;
                }
            }
        }
        for(int i = 0; i < cnt; i++){
            deg[i] = 0;
            for(int j = 0; j < 5; j++){
                int nx = x[i] + dx[j], ny = y[i] + dy[j];
                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;
}

DBFS(双向广度优先搜索,520ms):

#include<bits/stdc++.h>
using namespace std;
const int maxl = 20;
const int maxn = 150;
int w, h, n;
int deg[maxn];
int G[maxn][5];
int s[3], t[3];
int d[2][maxn][maxn][maxn];
int x[maxn], y[maxn], id[maxl][maxl];
int mark = 0;
char maze[maxl][maxl];
int dx[] = {0, 0, 0, -1, 1};
int dy[] = {0, 1, -1, 0, 0};

int ID(int a, int b, int c){
    return (a<<16) | (b<<8) | c;
}

bool conflict(int a, int b, int a2, int b2){
    return a2 == b2 || (a2 == b && b2 == a);
}

void expand(queue<int>& q, int n){
    int u = q.front(); q.pop();
    int a = (u>>16) & 0xff, b = (u>>8) & 0xff, c = u & 0xff;
    if(d[1-n][a][b][c] != -1) mark = d[1-n][a][b][c] + d[n][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(b, c, b2, c2)) continue;
                if(d[n][a2][b2][c2] != -1) continue;
                int t = ID(a2, b2, c2);
                q.push(t);
                d[n][a2][b2][c2] = d[n][a][b][c] + 1;
            }
        }
    }
}

int DBFS(){
    memset(d, -1, sizeof(d));
    queue<int>q[2];
    q[0].push(ID(s[0], s[1], s[2]));
    d[0][s[0]][s[1]][s[2]] = 0;
    q[1].push(ID(t[0], t[1], t[2]));
    d[1][t[0]][t[1]][t[2]] = 0;
    int order = 0;
    mark = 0;
    while(!q[0].empty() && !q[1].empty()){
        order ? expand(q[0], 0) : expand(q[1], 1);
        if(mark) return mark;
        order = !order;
    }
    return -1;
}

int main()
{
    // freopen("data.in","r",stdin);
    // freopen("data.out","w",stdout);
    while(scanf("%d%d%d", &w, &h, &n) == 3 && w){
        getchar();
        for(int i = 0; i < h; i++)
            fgets(maze[i], maxl, stdin);

        int 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;
                    if(isupper(maze[i][j])) t[maze[i][j] - 'A'] = cnt;
                    cnt++;
                }
            }
        }
        for(int i = 0; i < cnt; i++){
            deg[i] = 0;
            for(int j = 0; j < 5; j++){
                int nx = x[i] + dx[j], ny = y[i] + dy[j];
                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", DBFS());
    }

    return 0;
}

 

点赞