原题地址
http://noi.openjudge.cn/ch0205/1804/
1804:小游戏
总时间限制:
1000ms
内存限制:
65536kB
描述
一天早上,你起床的时候想:“我编程序这么牛,为什么不能靠这个赚点小钱呢?”因此你决定编写一个小游戏。
游戏在一个分割成w * h个正方格子的矩形板上进行。如图所示,每个正方格子上可以有一张游戏卡片,当然也可以没有。
当下面的情况满足时,我们认为两个游戏卡片之间有一条路径相连:
路径只包含水平或者竖直的直线段。路径不能穿过别的游戏卡片。但是允许路径临时的离开矩形板。下面是一个例子:
这里在 (1, 3)和 (4, 4)处的游戏卡片是可以相连的。而在 (2, 3) 和 (3, 4) 处的游戏卡是不相连的,因为连接他们的每条路径都必须要穿过别的游戏卡片。
你现在要在小游戏里面判断是否存在一条满足题意的路径能连接给定的两个游戏卡片。
输入
输入包括多组数据。一个矩形板对应一组数据。每组数据包括的第一行包括两个整数w和h (1 <= w, h <= 75),分别表示矩形板的宽度和长度。下面的h行,每行包括w个字符,表示矩形板上的游戏卡片分布情况。使用‘X’表示这个地方有一个游戏卡片;使用空格表示这个地方没有游戏卡片。
之后的若干行上每行上包括4个整数x1, y1, x2, y2 (1 <= x1, x2 <= w, 1<= y1, y2 <= h)。给出两个卡片在矩形板上的位置(注意:矩形板左上角的坐标是(1, 1))。输入保证这两个游戏卡片所处的位置是不相同的。如果一行上有4个0,表示这组测试数据的结束。
如果一行上给出w = h = 0,那么表示所有的输入结束了。
输出
对每一个矩形板,输出一行“Board #n:”,这里n是输入数据的编号。然后对每一组需要测试的游戏卡片输出一行。这一行的开头是“Pair m: ”,这里m是测试卡片的编号(对每个矩形板,编号都从1开始)。接下来,如果可以相连,找到连接这两个卡片的所有路径中包括线段数最少的路径,输出“k segments.”,这里k是找到的最优路径中包括的线段的数目;如果不能相连,输出“impossible.”。
每组数据之后输出一个空行。
样例输入
5 4
XXXXX
X X
XXX X
XXX
2 3 5 3
1 3 4 4
2 3 3 4
0 0 0 0
0 0
样例输出
Board #1:
Pair 1: 4 segments.
Pair 2: 3 segments.
Pair 3: impossible.
作为一道中(keng)规(dian)中(ju)矩(duo)的广搜题,这题真心能把不细心的人(比如我)虐出一口老血来……
point.1 读入
正确读法有好几种,但错误读法也不是那么显然的,所以可能会有人深陷读入bug的泥潭中无法自拔。常见错误读法如下:
1. while(cin>>m>>n)会导致第二张地图读不进去(每一组里的询问的读法同理);
2. cin>>s(s为字符串) 会导致空格被忽略;
3.cin>>ch (ch为字符) 会导致空格被忽略;
4. ch=getchar() (ch同上) 稍微改改可以,然而要注意手动忽略回车符’\n’;
正解读法用getchar()读字符,如果读到回车符就继续读。
point.2 终止条件(然而不考虑这一点竟然也能水过)
这道题并不是一道正经的模板型广搜,因为正版广搜第一次搜到终点时一定是最优解,然而这道题不同,因为如果不改变搜索路径方向,线段数是不更新的。因此搜索退出的条件只能是真的无路可走,而造成这种局面(避免循环无法终止最后T掉)的方法就是打标记——每个可行的位置都用一个二维数组记录到达该点的最少线段数,如果再次搜到这个点时发现当前解比原来的标记还大,说明没有更新的必要,这个点不可入队,否则就更新并入队。这样总有终止的时候,最后循环退出后判断终点的标记是否被更新过,如果更新过解就是此时该点的标记。
point.3 输出格式(PE的童鞋看过来)
每组数据之后输出一个空行。是每一张地图对应的询问都结束后另外输出一行空格,这是需要特殊判断的,而不是每一次询问输出后回车就行的。
point.4 初始化(5分的一种可能)
上文提到的标记数组,每次询问前都要赋成极大值;
读入的字符数组,每组数据前都要初始化为0。
解题思路
广搜,注意到上面提到的点的话应该比较好写了。
参考代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
const int xx[5]={0,0,1,0,-1};
const int yy[5]={0,1,0,-1,0};
int vis[1000][1000];
char a[1000][1000];
struct mc
{
intx,y,ans,dir;
}s,r;
int main()
{
intn,m,num=0,cnt;
cin>>m>>n;
while(m&&n)
{
num++;
cnt=0;
cout<<"Board#"<<num<<":\n";
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
a[i][j]=getchar();
while(a[i][j]=='\n')
a[i][j]=getchar();
}
}
intx1,y1,x2,y2;
cin>>y1>>x1>>y2>>x2;
while(y1&&x1&&y2&&x2)
{
memset(vis,0x3f,sizeof(vis));
queue<mc>q;
cnt++;
s.x=x1,s.y=y1;
r.x=x2,r.y=y2;
s.ans=0;
s.dir=0;
q.push(s);
boolff=0;
while(!q.empty())
{
intx=q.front().x;
inty=q.front().y;
intans=q.front().ans;
intdir=q.front().dir;
q.pop();
for(int i=1;i<=4;i++)
{
inttx=x+xx[i];
intty=y+yy[i];
mcp;
p.x=tx,p.y=ty,p.dir=i;
if(i==dir) p.ans=ans;
elsep.ans=ans+1;
if(tx<0||tx>n+1) continue;
if(ty<0||ty>m+1) continue;
if(a[tx][ty]=='X'&&(!(tx==r.x&&ty==r.y))) continue;
if(vis[tx][ty]<=p.ans) continue;
vis[tx][ty]=p.ans;
if(!(tx==r.x&&ty==r.y)) q.push(p);
//注意括号的位置,上面也一样;
}
}
if (vis[r.x][r.y]!=vis[99][99])
cout<<"Pair"<<cnt<<": "<<vis[r.x][r.y]<<"segments.\n";
else
cout<<"Pair "<<cnt<<":impossible.\n";
cin>>y1>>x1>>y2>>x2;
}
cin>>m>>n;
printf("\n");
}
}