Description
在一种”麻将”游戏中,游戏是在一个有W*H格子的矩形平板上进行的。每个格子可以放置一个麻将牌,也可以不放(如图所示)。玩家的目标是将平板上的所有可通过一条路径相连的两张相同的麻将牌,从平板上移去。最后如果能将所有牌移出平板,则算过关。
这个游戏中的一个关键问题是:两张牌之间是否可以被一条路径所连接,该路径满足以下两个特性:
1. 它由若干条线段组成,每条线段要么是水平方向,要么是垂直方向。
2. 这条路径不能横穿任何一个麻将牌 (但允许路径暂时离开平板)。
这是一个例子:
在(1,3)的牌和在(4, 4)的牌可以被连接。(2, 3)和(3, 4)不能被连接。
你的任务是编一个程序,检测两张牌是否能被一条符合以上规定的路径所连接。
Input
输入文件的第一行有两个整数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,则表示输入结束。
Output
输出文件中,对于每一对牌输出占一行,为连接这一对牌的路径最少包含的线段数。如果不存在路径则输出0。
Sample Input
5 4
XXXXX
X X
XXX X
XXX
2 3 5 3
1 3 4 4
2 3 3 4
0 0 0 0
Sample Output
4
3
0
算法分析:
这道题可以参考上一篇文章:最少转弯数
最少转弯数这一道题计算出发点到目的点的最少转弯数,本题计算两个点之间的路径的线段的段数。这个段数其实就是转弯数加1.所以可以按照上一篇文章的做法求解。这一题当中,麻将牌可以离开平板,所以在表示平板的0/1矩阵地图周围构造一个环形通路,也就是外面有一圈0,以便广搜时可以走这一条路。
大体的思路就是在广搜时,对每一个可以前进的方向都一直前进直到不可行走为止,然后再从下一个方向继续广搜。具体描述见最少转弯数这篇文章的分析和代码注释。
PS:本题部分资料整理自下面两篇文章。
下面的代码是由最少转弯数的代码修改而来:
1 #include<cstdio>
2 #include<cstring>
3 #include<queue>
4 using namespace std;
5
6 const int dx[]={0,1,0,-1};//右下左上
7 const int dy[]={1,0,-1,0};
8 struct point
9 {
10 int x,y,turn;
11 }_begin,_end,p;
12
13 queue<point> q;
14 int n,m,_map[101][101];
15 bool used[101][101];
16
17 int fun();//统计从 _begin到达 _end的最少拐弯数。拐弯数加1就是线段数。
18
19 int main()
20 {
21 freopen("MAHJONG_data/MAHJONG7.in","r",stdin);
22 freopen("MAHJONG_data/MAHJONG7.ans","w",stdout);
23 int w,h,i,j,x1,y1,x2,y2;
24 char ch;
25 scanf("%d%d",&w,&h);getchar();//吸收行末尾回车符
26 n=h;m=w; // n行,m列
27
28 //下面构造地图,把原来的字符矩阵转换为0/1矩阵表示的地图
29 //构造地图周围的环形通路
30 for(i=0;i<=n+1;i++) { _map[i][0]=0; _map[i][m+1]=0; }
31 for(j=0;j<=m+1;j++) { _map[0][j]=0; _map[n+1][j]=0; }
32 //for(j=0;j<=m+2;j++) _map[n+2][j]=1; 构造矩阵底部围墙
33 //for(i=0;i<=n+2;i++) _map[i][m+2]=1; 构造矩阵右侧围墙
34 for(i=1;i<=n;i++)//输入地图,把地图转换为0/1矩阵
35 {
36 for(j=1;j<=m;j++)
37 {
38 ch=getchar();
39 if(ch==' ') _map[i][j]=0; //0表示可以行走
40 else _map[i][j]=1; //1表示不可以行走
41 }
42 getchar();//吸收行末尾回车符
43 }
44 n++; m++;//因为环形通路的存在,行和列数目都增加 1。
45
46 /*for(i=0;i<=n;i++)
47 {
48 for(j=0;j<=m;j++) printf("%d",_map[i][j]);
49 printf("\n");
50 }
51 printf("\n-----------\n");*/
52
53 while(scanf("%d%d%d%d",&x1,&y1,&x2,&y2)!=EOF)
54 {
55 if(x1==0&&x2==0&&y1==0&&y2==0) break;
56 else
57 {
58 _begin.x=y1;
59 _begin.y=x1;
60 _end.x=y2;
61 _end.y=x2;
62
63 _map[y2][x2]=0;//要使得目的地可以到达
64 fun();
65 _map[y2][x2]=1;//还原地图
66 }
67 }
68
69 return 0;
70 }
71 int fun()
72 {
73 memset(used,0,sizeof(used));
74 while(!q.empty()) q.pop();//清空队列
75
76 q.push(_begin);
77 q.front().turn=0;
78 while(!q.empty())
79 {
80 for(int i=0;i<4;i++)
81 {
82 p.x=q.front().x+dx[i];
83 p.y=q.front().y+dy[i];
84 //从队头出发往dx[i]和dy[i]方向一直走,直到遇到边界或高山则停止,再从原队头换下一个方向走
85 while(p.x>=0&&p.x<=n&&p.y>=0&&p.y<=m&&!_map[p.x][p.y]) //环形通路也是可以行走的
86 {
87 if(!used[p.x][p.y])
88 {
89 if(p.x==_end.x&&p.y==_end.y)
90 {
91 printf("%d\n",q.front().turn+1);//注意输出的是当前队头的转弯次数。
92 return 0;
93 }
94 used[p.x][p.y]=1;
95 p.turn=q.front().turn+1;
96 q.push(p);
97 }
98 p.x+=dx[i];
99 p.y+=dy[i];
100 }
101 }
102 q.pop();//当前队头元素已经不能再扩展,可以删除队头
103 }
104 printf("0\n");//假如广搜无法到达目的地,那么输出0
105 }