I'm stuck! 一篇查完错了的小白题解

CCF 201312-5 I’m stuck (C++)

作为一个刚开始刷CCF、算法入门的小白,花了两天搞懂BFS(Breadth First Search 广度优先搜索)并查了几篇此题的write-up,最后写出来自己认为最合理的代码。
题目如下,后面有身为小白的基本思路详解。

问题描述
  给定一个R行C列的地图,地图的每一个方格可能是’#’, ‘+’, ‘-‘, ‘|’, ‘.’, ‘S’, ‘T’七个字符中的一个,分别表示如下意思:
  ‘#’: 任何时候玩家都不能移动到此方格;
  ‘+’: 当玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非’#’方格移动一格;
  ‘-‘: 当玩家到达这一方格后,下一步可以向左右两个方向相邻的一个非’#’方格移动一格;
  ‘|’: 当玩家到达这一方格后,下一步可以向上下两个方向相邻的一个非’#’方格移动一格;
  ‘.’: 当玩家到达这一方格后,下一步只能向下移动一格。如果下面相邻的方格为’#’,则玩家不能再移动;
  ‘S’: 玩家的初始位置,地图中只会有一个初始位置。玩家到达这一方格后,下一步可以向上下左右四个方向相邻的任意一个非’#’方格移动一格;
  ‘T’: 玩家的目标位置,地图中只会有一个目标位置。玩家到达这一方格后,可以选择完成任务,也可以选择不完成任务继续移动。如果继续移动下一步可以向上下左右四个方向相邻的任意一个非’#’方格移动一格。
  此外,玩家不能移动出地图。
  请找出同时满足下面两个性质的方格个数:
  1. 玩家可以从初始位置移动到此方格;
  2. 玩家不可以从此方格移动到目标位置。
输入格式
  输入的第一行包括两个整数R 和C,分别表示地图的行和列数。(1 ≤ R, C ≤ 50)。
  接下来的R行每行都包含C个字符。它们表示地图的格子。地图上恰好有一个’S’和一个’T’。
输出格式
  如果玩家在初始位置就已经不能到达终点了,就输出“I’m stuck!”(不含双引号)。否则的话,输出满足性质的方格的个数。
样例输入
5 5
–+-+
..|#.
..|##
S-+-T
样例输出
2

因为地图的长宽限制范围较小,尝试使用bfs广度优先搜索算法。
基本思路:
1.从起点开始广度优先探索地图,在reach数组中把能够到达的位置标记为1,同时记录路径为反向搜索做准备。
2.从终点开始广度优先搜索,只有路径已经在正向搜索时记录过时才可以行走,同时在able数组中标记能够到达的位置,表示可以从此处到达终点。
3.如果终点reach为false,输出“I‘m stuck!”,否则:
4.遍历reach和able数组,遇到从起点能够到达却不能到达终点的位置时,ans++.
5.输出答案。
准备工作:
设置控制前进方向的数组ADDx,ADDy,此为迷宫题常用的手法。本题中和自定义的get函数结合,可以根据当前地图符号返回起点与终点,限制行走方向。
定义reach,able数组,设置时初始化为0。
自定义结构体Point,包括横纵坐标x,y,定义队列Q存储需要搜索的结点。
定义map《Point,bool》,用于检查当前结点是否已经在队列中。注意使用自定义类型作为map的键值时需要重载小于号。
操作:
输入数据时记录起点和终点的Point,以便以后计算使用。
先让起点入队列,从起点开始搜索。在队列中有待搜索的结点时:在for循环中尝试了四个方向,每走一步就判断此点是否在地图上,是则标记reach、标记路径,并再次判断是否已在队列中,不是则加入队列待下一步搜索。//Forward
队列已空,让终点入队列,几乎重复从起点搜索的步骤,只把标记改成able数组,并更改结点入队列的判断条件。//Backward
检查结果,输出答案。
注:
查出来一个zz错误(现已更正):In函数的判断越界条件那里,把行数R(row)和列数C(column)写反了,导致第一次没有满分。也提醒新手写二维数组的循环的时候记住自己设的变量到底是代表行还是列,比如一般C[x][y]代表的是x行y列的元素。
希望能对同样小白的朋友理解算法有所帮助~

/*
ccf 201312-5
resdust
*/
#include <iostream>
#include <queue>
#include <map>
#include <string>
#include <map>
#define MAX 50
using namespace std;

const int ADDx[4] = { 1, -1, 0, 0 };
const int ADDy[4] = { 0, 0, 1, -1 };

char Map[MAX][MAX] = { 0 }; //original map
bool reach[MAX][MAX] = { 0 }; //记录从起点能否到达该点
bool able[MAX][MAX] = { 0 }; //记录从该点能否到达终点 

class Point {
public:
    int x, y;
    bool operator==(const Point p) const {
        if (p.x == x && p.y == y) { return true; }
        return false;
    }
    bool operator < (const Point p)const {
        if (x != p.x) { return x < p.x; }
        else { return y < p.y; }
    }
};

bool road[MAX][MAX][MAX][MAX] = { 0 };
//road[x][y][i][j], from map[x][y] can move to map[i][j]
int C, R; 
//to save Column and Row
queue<Point> Q;
//to save the Points to be searched (bfs)

void get(Point p, int &begin, int &end) {
    //用begin和end记录该点p可以走的方向(映射到两个ADD数组)
    char temp = Map[p.x][p.y];
    if (temp == 'S' || temp == 'T' || temp == '+') {
        begin = 0, end = 3;
    }
    else if (temp == '-') {
        begin = 2, end = 3;
    }
    else if (temp == '|') {
        begin = 0, end = 1;
    }
    else if (temp == '.') {
        begin = end = 0;
    }
}

bool In(int x, int y) {
    //判断该点在地图上并且不是'#',即可以到达
    if (x < R && y < C && x >= 0 && y >= 0 && Map[x][y] != '#')
        { return true; }
    else { return false; }
}

void Forward() {
    //to find where the start point can reach
    //and reserve the feasible road
    map<Point, bool> V;
    //to save the existing Points in queue, avoid repeatition
    V[Q.front()] = true;
    while (!Q.empty()) {
        int f = 0, b = 0;
        Point temp = Q.front(); Q.pop();
        get(temp, f, b);
        for (int i = f; i <= b; i++) {
            Point u = temp;
            u.x += ADDx[i];
            u.y += ADDy[i];
            if (In(u.x, u.y)) {
                reach[u.x][u.y] = 1;
                road[temp.x][temp.y][u.x][u.y] = 1; // conserve the road 
                if (!V[u]) {
                    Q.push(u);
                    V[u] = true;
                }
            }
        }
    }

}

void Backward() {
    map<Point, bool> V;
    //to save the existing Points in queue, avoid repeatition
    V[Q.front()] = true;
    while (!Q.empty()) {
        Point temp = Q.front(); Q.pop();
        for (int i = 0; i < 4; i++) {
            Point u = temp;
            u.x += ADDx[i];
            u.y += ADDy[i];
            if (In(u.x, u.y) && road[u.x][u.y][temp.x][temp.y]) {
                able[u.x][u.y] = 1;
                if (!V[u]) {
                    Q.push(u);
                    V[u] = true;
                }
            }
        }
    }
    return;
}

int main()
{
    cin >> R >> C;
    Point s, t; //start point and target point
    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) {
            cin >> Map[i][j];
            if (Map[i][j] == 'S') { s.x = i; s.y = j; }
            if (Map[i][j] == 'T') { t.x = i; t.y = j; }
        }
    }

    Q.push(s);
    Forward();

    Q.push(t);
    Backward();

    int ans = 0;
    for (int i = 0; i < R; i++) {
        for (int j = 0; j < C; j++) {
            if (reach[i][j] && !able[i][j]) { ans++; }
        }
    }
    if (reach[t.x][t.y] == false) { cout << "I'm stuck!" << endl; }
    else {  cout << ans << endl; }

    return 0;
}

“`

猜你喜欢

转载自blog.csdn.net/resdust/article/details/82531917