题目

在一种"麻将"游戏中,游戏是在一个有W*H格子的矩形平板上进行的。每个格子可以放置一个麻将牌,也可以不放(如图所示)。玩家的目标是将平板上的所有可通过一条路径相连的两张相同的麻将牌,从平板上移去。最后如果能将所有牌移出平板,则算过关。

  这个游戏中的一个关键问题是:两张牌之间是否可以被一条路径所连接,该路径满足以下两个特性:

  1. 它由若干条线段组成,每条线段要么是水平方向,要么是垂直方向。

  2. 这条路径不能横穿任何一个麻将牌 (但允许路径暂时离开平板)。

  这是一个例子:

java游戏三国大富翁模式打麻将 三国大富翁桌游规则_广度优先搜索

     

  在(1,3)的牌和在(4, 4)的牌可以被连接。(2, 3)和(3, 4)不能被连接。

  你的任务是编一个程序,检测两张牌是否能被一条符合以上规定的路径所连接。

输入

第一行有两个整数w,h(1<=w,h<=75),表示平板的宽和高。接下来h行描述平板信息,每行包含w个字符,如果某格子有一张牌,则这个格子上有个'X',否则是一个空格。平板上最左上角格子的坐标为(1,1),最右下角格子的坐标为(w,h)。接下来的若干行,每行有四个数x1, y1, x2, y2 ,且满足1<=x1,x2<=w,1<=y1,y2<=h,表示两张牌的坐标(这两张牌的坐标总是不同的)。如果出现连续四个0,则表示输入结束。

输出

对于每一对牌输出占一行,为连接这一对牌的路径最少包含的线段数。如果不存在路径则输出0。

样例输入

5 4
XXXXX
X   X
XXX X
 XXX 
2 3 5 3
1 3 4 4
2 3 3 4
0 0 0 0

样例输出

4
3
0

解题思路

广度优先搜索,我们可以从起点开始,选择一个前进方向,然后径直往前走,对于这个过程中我们所经过的点,都是位于一条线段上的。将位于同一条线段上的点都压入到队列中,作为下一次的起点,反复操作,最终到达终点的线段数就是最优解。

解题代码

#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int maxn = 100;
int w,h,x1,y1,x2,y2;
int mat[maxn][maxn], segnumber[maxn][maxn];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
struct node{
    int x,y;
    int depth;
};

int bfs() {
    memset(segnumber, 0, sizeof(segnumber));
    queue<node> q;
    node a;
    a.x = x1;
    a.y = y1;
    a.depth = 0;
    q.push(a);
    while(!q.empty()) {
        a = q.front();
        q.pop();
        for (int i = 0; i < 4; ++i) {
            int tx = a.x + dx[i];
            int ty = a.y + dy[i];
            // 朝一个方向走到底,这个方向上的所有可行点都是位于同一条线段上的。
            while (tx >= 0 && tx <= h+1 && ty >= 0 && ty <= w+1 && (segnumber[tx][ty] == 0 || segnumber[tx][ty] == a.depth+1)) {
                if (tx == x2 && ty == y2) {
                    return a.depth+1;
                }
                if (mat[tx][ty] == 'X') {
                    break;
                }
                segnumber[tx][ty] = a.depth+1;
                node b;
                b.x = tx;
                b.y = ty;
                b.depth = a.depth+1;
                q.push(b);
                tx += dx[i];
                ty += dy[i];
            }
        }
    }
    return 0;
}

int main() {
    scanf("%d%d", &w, &h);
    getchar();
    for (int i = 1; i <= h; ++i) {
        for (int j = 1; j <= w; ++j) {
            scanf("%c", &mat[i][j]);
        }
        getchar();
    }
    // 在地图的四周都补上一圈空白可行区域
    for (int i = 0; i <= h+1; ++i) {
        mat[i][0] = ' ';
        mat[i][w+1] = ' ';
    }
    for (int i = 0; i <= w+1; ++i) {
        mat[0][i] = ' ';
        mat[h+1][i] = ' ';
    }
    while(~scanf("%d%d%d%d", &y1, &x1, &y2, &x2) && (x1 || y1 || x2 || y2)) {
        printf("%d\n", bfs());
    }
    return 0;
}