题目描述
在一种“麻将”游戏中,游戏是在一个有W×H格子的矩形平板上进行的。每个格子可以放置一个麻将牌,也可以不放(如图所示)。玩家的目标是将平板上的所有可通过一条路径相连的两张相同的麻将牌,从平板上移去。最后如果能将所有牌移出平板,则算过关。
这个游戏中的一个关键问题是:两张牌之间是否可以被一条路径所连接,该路径满足以下两个特性:
1、它由若干条线段组成,每条线段要么是水平方向,要么是垂直方向。
2、这条路径不能横穿任何一个麻将牌(但允许路径暂时离开平板)。
这是一个例子:
在(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
题解
依照题意,可以将题目转换成由多条线段相连而形成的路中,满足其中有线段与麻将(x1,y1)、麻将(x2,y2)相邻。
我们可以把横坐标(纵坐标)相同的两个麻将之间的水平(垂直)距离看做一条线段,又可以转换成由多条线段相交而形成的路中,满足其中有线段与麻将(x1,y1)、麻将(x2,y2)相邻。这样就可以很方便地用搜索做出来了(其实dp应该也可以)。
注意这里的坐标输入时可能会有一点障碍,可以将题目中的横纵坐标交换。
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #define MAX_N 75 #define MAX_M 75 #define INF 10000000 #define Min(x, y) (x <= y ? x : y) #define Max(x, y) (x >= y ? x : y) using namespace std; int n, m; int a[MAX_N + 3][MAX_M + 3]; int s[MAX_N + 3][MAX_M + 3][4]; // segment const int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1}; int ANS = INF; void Init(); void BFS(int, int, int, int); int main() { scanf("%d%d", &m, &n); n += 2, m += 2; char ch; for(register int i = 2; i < n; ++i) { if(ch ^ ' ') getchar(); for(register int j = 2; j < m; ++j) { ch = getchar(); a[i][j] = (ch == 'X'); if(ch == ' ') break; } } Init(); int sx, sy, tx, ty; while(scanf("%d%d%d%d", &sy, &sx, &ty, &tx) && (sx || sy || tx || ty)) { ++sx, ++sy, ++tx, ++ty; BFS(sx, sy, tx, ty); printf("%d ", (ANS == INF ? 0 : ANS)); } return 0; } void Init() { int lt, rt; for(register int i = 1; i <= m; ++i) { lt = rt = 1; while(lt <= n) { while(a[lt][i] && lt <= n) ++lt; rt = lt + 1; while(!a[rt][i] && rt <= n) ++rt; for(register int j = lt; j < rt; ++j) { s[j][i][0] = lt; s[j][i][1] = rt - 1; } lt = rt + 1; } } for(register int i = 1; i <= n; ++i) { lt = rt = 1; while(lt <= m) { while(a[i][lt] && lt <= m) ++lt; rt = lt + 1; while(!a[i][rt] && rt <= m) ++rt; for(register int j = lt; j < rt; ++j) { s[i][j][2] = lt; s[i][j][3] = rt - 1; } lt = rt + 1; } } return; } inline void BFS(int sx, int sy, int tx, int ty) { queue<int> qx, qy, qd; //x, y, direction int f[MAX_N + 3][MAX_M + 3][2]; memset(f, 0, sizeof f); for(register int i = 0; i < 4; ++i) { if(!a[sx + dx[i]][sy + dy[i]]) { if(i < 2) { qx.push(s[sx + dx[i]][sy + dy[i]][0]); qy.push(sy + dy[i]); qd.push(0); f[s[sx + dx[i]][sy + dy[i]][0]][sy + dy[i]][0] = 1; } else { qx.push(sx + dx[i]); qy.push(s[sx + dx[i]][sy + dy[i]][2]); qd.push(1); f[sx + dx[i]][s[sx + dx[i]][sy + dy[i]][2]][1] = 1; } } } int x, y, d; int ans = 0; ANS = INF; while(!qx.empty()) { ++ans; if(ans > ANS) break; for(register int I = qx.size(); I; --I) { x = qx.front(); y = qy.front(); d = qd.front(); qx.pop(); qy.pop(); qd.pop(); if(d) { if(tx == x && (ty + 1 == y || ty - 1 == s[x][y][3])) ANS = Min(ANS, ans); if((tx - 1 == x || tx + 1 == x) && ty >= y && ty <= s[x][y][3]) ANS = Min(ANS, ans + 1); for(register int i = s[x][y][3]; i >= y; --i) { if(!(x - 1 >= 1 && !a[x - 1][i] || x + 1 <= n && !a[x + 1][i])) continue; if(!f[s[x][i][0]][i][0]) { f[s[x][i][0]][i][0] = 1; qx.push(s[x][i][0]); qy.push(i); qd.push(0); } } } else { if(ty == y && (tx + 1 == x || tx - 1 == s[x][y][1])) ANS = Min(ANS, ans); if((ty + 1 == y || ty - 1 == y) && tx >= x && tx <= s[x][y][1]) ANS = Min(ANS, ans + 1); for(register int i = s[x][y][1]; i >= x; --i) { if(!(y - 1 >= 1 && !a[i][y - 1] || y + 1 <= m && !a[i][y + 1])) continue; if(!f[i][s[i][y][2]][1]) { f[i][s[i][y][2]][1] = 1; qx.push(i); qy.push(s[i][y][2]); qd.push(1); } } } } } return; }
其实这样的做法貌似是麻烦了一些,是有更好的方法。我们可以尝试遍历到每一个点,就把这个点所处的线段的所有点都遍历一次,插入bfs队列,这样就可以当做是拐弯了,比前面的方法更快。
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #define MAX_N 75 #define MAX_M 75 #define Min(x, y) (x <= y ? x : y) using namespace std; int n, m; int a[MAX_N + 3][MAX_M + 3]; void Init(); int BFS(int, int, int, int); int main() { scanf("%d%d", &m, &n); n += 2, m += 2; char ch; for(register int i = 2; i < n; ++i) { if(ch ^ ' ') getchar(); for(register int j = 2; j < m; ++j) { ch = getchar(); a[i][j] = (ch == 'X'); if(ch == ' ') break; } } int sx, sy, tx, ty; while(scanf("%d%d%d%d", &sy, &sx, &ty, &tx) && (sx || sy || tx || ty)) { ++sx, ++sy, ++tx, ++ty; printf("%d ", BFS(sx, sy, tx, ty)); } return 0; } inline int BFS(int sx, int sy, int tx, int ty) { queue<int> qx, qy; int f[MAX_N + 3][MAX_M + 3]; memset(f, 0, sizeof f); qx.push(sx); qy.push(sy); int x, y; int ans = 0; while(!qx.empty()) { ++ans; for(register int I = qx.size(); I; --I) { x = qx.front(); y = qy.front(); qx.pop(); qy.pop(); for(register int i = x - 1; i; --i) { if(f[i][y]) continue; if(a[i][y]) { if(i == tx && y == ty) return ans; break; } f[i][y] = 1; qx.push(i); qy.push(y); } for(register int i = x + 1; i <= n; ++i) { if(f[i][y]) continue; if(a[i][y]) { if(i == tx && y == ty) return ans; break; } f[i][y] = 1; qx.push(i); qy.push(y); } for(register int i = y - 1; i; --i) { if(f[x][i]) continue; if(a[x][i]) { if(x == tx && i == ty) return ans; break; } f[x][i] = 1; qx.push(x); qy.push(i); } for(register int i = y + 1; i <= m; ++i) { if(f[x][i]) continue; if(a[x][i]) { if(x == tx && i == ty) return ans; break; } f[x][i] = 1; qx.push(x); qy.push(i); } } } return 0; }