Skip to content

14143: 【原4143】推箱子

题目

题目描述

author: Konpaku 原OJ链接:https://acm.sjtu.edu.cn/OnlineJudge-old/problem/4143

问题描述

推箱子是一款经典的小游戏。游戏要求玩家将若干个箱子推到指定的位置,并以箱子移动次数最少作为目标。 现在,我们只需要考虑一个简化的版本——只有一个箱子。对于一张给定的地图,你需要判断是否可以将箱子推到目标位置,如果可以,你还需要求出箱子最少的移动次数。

输入格式

第一行有两个用单个空格分隔的正整数\(n,m(n,m<=100)\),表示输入一张\(n*m\)的地图。 接下来\(n\)行,每行\(m\)个字母,字母分别是S, M, P, K, w,意义如下: S – 表示地图中的墙 M – 表示玩家的初始位置 P – 表示箱子的初始位置 K- 表示箱子的目标位置 w – 表示地图中的空格 M, P 和 K 在文件中只出现一次

输出格式

如果箱子不能移动到目的位置,则输出NO。 如果箱子能移动到目的位置,则输出最小的移动次数。

样例输入

\\(10\\) \\(12\\)
SSSSSSSSSSSS
SwwwwwwwSSSS
SwSSSSwwSSSS
SwSSSSwwSKSS
SwSSSSwwSwSS
SwwwwwPwwwww
SSSSSSSwSwSw
SSSSSSMwSwww
SSSSSSSSSSSS
SSSSSSSSSSSS

样例输出

\\(7\\)

数据范围

对于\(10\%\)的数据: \(n=1\)或\(m=1\) 另\(10\%\)的数据: \(n=5,m=100\)或\(n=100,m=5\) 对于\(30\%\)的数据: \(n,m<=50\) 对于\(100\%\)的数据: \(n,m<=100\)

zqy2018's solution

/*
    Warning: maybe not a good solution
    See the editorial at https://github.com/zqy1018/sjtu_oj_solutions/blob/master/solutions/sjtu4143.md
*/
#include <bits/stdc++.h>
#define INF 2000000000
using namespace std;
typedef long long ll;
int read(){
    int f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -f; c = getchar();}
    while(c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar();
    return f * x; 
}
int n, m;
int vis[105][105] = {0};
bool ok[105][105][4][4] = {0}, made[105][105] = {0};
int st[105][105][5] = {0};
char grid[105][105];
const int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
queue<int> qx, qy;
queue<pair<pair<int, int>, int> > q;
void valid(int bx, int by, int curx, int cury){
    memset(vis, 0, sizeof(vis));
    qx.push(curx), qy.push(cury);
    vis[curx][cury] = 1;
    while (!qx.empty()){
        int cx = qx.front(), cy = qy.front();
        qx.pop(), qy.pop();
        for (int i = 0; i < 4; ++i){
            int ex = cx + dx[i], ey = cy + dy[i];
            if (ex < 0 || ex >= n || ey < 0 || ey >= m)
                continue;
            if (ex == bx && ey == by) continue;
            if (vis[ex][ey] || grid[ex][ey] == 'S') continue;
            vis[ex][ey] = 1;
            qx.push(ex), qy.push(ey);
        }
    }
}
void valid2(int bx, int by){
    memset(vis, 0, sizeof(vis));
    made[bx][by] = true;
    int lis[4], lislen;
    for (int i = 0; i < 4; ++i){
        int x = bx + dx[i], y = by + dy[i];
        if (x < 0 || x >= n || y < 0 || y >= m)
            continue;
        if (vis[x][y] || grid[x][y] == 'S') continue;
        qx.push(x), qy.push(y);
        vis[x][y] = i + 1;
        while (!qx.empty()){
            int cx = qx.front(), cy = qy.front();
            qx.pop(), qy.pop();
            for (int j = 0; j < 4; ++j){
                int ex = cx + dx[j], ey = cy + dy[j];
                if (ex < 0 || ex >= n || ey < 0 || ey >= m)
                    continue;
                if (ex == bx && ey == by) continue;
                if (vis[ex][ey] || grid[ex][ey] == 'S') continue;
                vis[ex][ey] = i + 1;
                qx.push(ex), qy.push(ey);
            }
        }
        lislen = 0;
        for (int j = 0; j < 4; ++j){
            int ex = bx + dx[j], ey = by + dy[j];
            if (ex < 0 || ex >= n || ey < 0 || ey >= m)
                continue;
            if (vis[ex][ey] == i + 1)
                lis[lislen++] = j;
        }
        for (int j = 0; j < lislen; ++j)
            for (int t = 0; t < lislen; ++t)
                ok[bx][by][lis[j]][lis[t]] = true;
    }
}
void init(){
    n = read(), m = read();
    for (int i = 0; i < n; ++i)
        scanf("%s", grid[i]);
}
void solve(){
    int stx = -1, sty = -1;
    int renx = -1, reny = -1;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < m; ++j){
            if (grid[i][j] == 'P'){
                stx = i, sty = j;
            }
            if (grid[i][j] == 'M'){
                renx = i, reny = j;
            }
        }
    valid(stx, sty, renx, reny);
    memset(st, 0x3f, sizeof(st));
    for (int i = 0; i < 4; ++i){
        int ex = stx + dx[i], ey = sty + dy[i];
        if (ex < 0 || ex >= n || ey < 0 || ey >= m)
            continue;
        if (!vis[ex][ey]) continue;
        q.push(make_pair(make_pair(stx, sty), i));
        st[stx][sty][i] = 1;
    }
    int ans = 0x3f3f3f3f;
    while (!q.empty()){
        auto pp = q.front();
        q.pop();
        int oppo = (pp.second ^ 1);
        int ccx = pp.first.first, ccy = pp.first.second;
        int cx = ccx + dx[oppo], cy = ccy + dy[oppo];
        // new posi for box
        if (cx < 0 || cx >= n || cy < 0 || cy >= m)
            continue;
        if (grid[cx][cy] == 'S') continue;
        if (grid[cx][cy] == 'K'){
            // final
            ans = st[ccx][ccy][pp.second];
            break;
        }
        if (!made[cx][cy])
            valid2(cx, cy);
        int cur_step = st[ccx][ccy][pp.second];
        for (int i = 0; i < 4; ++i){
            if (!ok[cx][cy][pp.second][i])
                continue;
            if (st[cx][cy][i] <= cur_step + 1)
                continue;
            st[cx][cy][i] = cur_step + 1;
            q.push(make_pair(make_pair(cx, cy), i));
        }
    }
    if (ans == 0x3f3f3f3f)
        printf("NO\n");
    else printf("%d\n", ans);
}
int main(){
    init();
    solve();
    return 0;
}