Have Lunch Together

来源:互联网 发布:movielens数据集下载 编辑:程序博客网 时间:2024/06/08 13:30

题意分析

给定一幅字符表示的地图,其中包含有 1 个起点'H',若干个座位'S',墙壁'#'和行人'P'

其中墙壁'#'和行人'P'是不可通过的区域。

假设在地图中,只能沿着上下左右移动,且每移动一个单元格为 1 步。

询问从'H'点出发,是否能够到达两个相邻的'S',且需要移动的步数最少是多少。

算法分析

从题目当中,我们就可以知道本题需要做什么:

  1. 读取字符地图,并找到起点的位置。

  2. 从起点开始,遍历该地图,记录到达每一个'S'的距离。

  3. 判断是否有两个相邻的'S'都可达,若存在多个解,则需要找到最小的值。

那么我们就按照这三个步骤来解决这道题目。


首先是数据的读入,由于输入数据中已经明确的告诉了我们地图为 NM 列,所以我们只需要一行一行读入字符串,并使用char map[N][M]保存该地图。

map[i][j]表示原地图的第i行第j列的信息。

之后再对整个map[i][j]进行一次 O(mn) 的遍历,找出起点的位置,并记录下来。

我们用startX, startY 来记录起点的坐标。

startX = startY = 0;// 读入地图for (int i = 1; i <= N; i++)     scanf("%s", map[i] + 1);// 查找起点Hfor (int i = 1; i <= N; i++)     for (int j = 1; j <= M; ++j)        if (map[i][j] == 'H') {            startX = i, startY = j;            break;        }


第二步,寻找从起点(startX, startY)分别到每个'S'的最短路径。这一步我们直接使用BFS对整个图进行一次遍历。

首先建立数组int step[N][M]step[i][j]表示从起点到(i,j)的最少步数。

初始化为step[i][j] = INT_MAX,默认为任何点都无法到达。

开始遍历时,将step[ startX ][ startY ]设定为0,并以(startX, startY)开始BFS整个地图。

在遍历整个地图的过程中我们需要注意:

  • map[i][j] = '#'map[i][j] = 'P'时,step[i][j]直接等于INT_MAX,并且不扩展新的节点。

  • map[i][j] = 'S'时,我们需要更新当前节点的step[i][j]信息,但是由于当小Hi和小Ho走到位置后就不会再进行移动,所以也不扩展新的节点。

最后当没有新的节点可以到达时,退出BFS,得到整个地图的step[N][M]

bool inMap(int x, int y) {    // 在地图内 && 不为墙壁同时也不为行人    return (1 <= x && x <= N && 1 <= y && y <= M) && (map[x][y] == '.' || map[x][y] == 'S');}const int dir[4][2] = { {0, -1}, {1, 0}, {0, 1}, {-1, 0} }; // 方向数组vector< pair<int, int> > seq;    // 用一个vector来存储BFS的队列void BFS(int startX, int startY) {    // 将起点存入队列    step[ startX ][ startY ] = 0;    seq.push_back( make_pair(startX, startY) );    int i = 0;    while (i < (int) seq.size()) {        for (int dr = 0; dr < 4; ++dr) {            // 扩展新的节点            int tempX = seq[i].first  + dir[dr][0];            int tempY = seq[i].second + dir[dr][1];            if (inMap(tempX, tempY) && step[tempX][tempY] == INT_MAX) {                step[tempX][tempY] = step[ seq[i].first ][ seq[i].second ] + 1;                // 当发现是座位时,不再进行扩展                if (map[tempX][tempY] != 'S') seq.push_back( make_pair(tempX, tempY) );            }        }        ++i;    }    return ;}


最后一步判断是否有两个连续的'S'都可达。

此时我们仍然遍历整个地图,因为只是检查是否有相邻的'S',不需要考虑顺序,所以我们按照i = 1..n, j = 1..m的顺序就可以。

当我们扫描到一个'S'时,首先判定其周围是否还有其他'S'。由于对称性,我们只需要检查两个方向即可。

若存在,则表示这两个'S'相邻,此时我们检查这两个位置的step值。

如果两个位置的step值都不等于INT_MAX,则说明这两个位置都是可以到达的。我们根据这两个位置的step和更新最优解。

当遍历完整个地图后,也就找到了我们所需要寻找的最优值。

int ret = INT_MAX;for (int i = 1; i <= N; ++i)    for (int j = 1; j <= M; ++j)        // 当前位置为S,且可以到达        if (map[i][j] == 'S' && step[i][j] != 0) {            // 检查下边是否有相邻S            if (map[i - 1][j] == 'S' && step[i - 1][j] != 0 && ans > step[i][j] + step[i - 1][j])                ret = step[i][j] + step[i - 1][j];            // 检查右边是否有相邻S            if (map[i][j - 1] == 'S' && step[i][j - 1] != 0 && ans > step[i][j] + step[i][j - 1])                ret = step[i][j] + step[i][j - 1];        }
0 0