Problem
题目描述
Bessie 在一个冰封的湖面上游泳,湖面可以表示为二维的平面,坐标范围是-1,000,000,000…1,000,000,000。
湖面上的N(1 <= N <= 20,000)个位置有石块(编号分别为1到N),其它位置是冰面。
由于Bessie滑冰技术不够好,她通过推动自己旁边的石块,依靠反作用力向某一个方向前进,在碰到一个新的石块之前,Bessie是不会停下来的。(当然,最后会停留在某块石块的前一个格子里)由于Bessie无法计算复杂的角度,她只能够向东南西北四个方向前进。
很显然,Bessie不能够穿越石块,因此,Bessie仅仅可以向三个方向滑。
滑冰不是没有风险,Bessie滑向某个方向后必须能碰到某个石块,因此她必须很小心。
考虑下面的一个情况,Bessie希望到达在她东面的目标位置(x=5,y=1),(. = 冰块,* = 石头, B = Bessie, G = 目的位置)如果她直接向东滑,那么她会滑过目标位置,因为她通过撞上某块石头来停下来,一个能够到达目标位置的方案是这样的:
(a) (b) © (d)
4 …. …. …. ….
3 …… slide …… slide …… slide ……
2 …* north …B…* east …B* south …*
1 .B…G. ------> .…G. ------> .…G. ------> .…B.
0 …. …. …. ….
0123456
在(a)中,Bessie 只能朝向北,东,或者南三个方向,但是只有在北面能撞上石头从而停下来,在(b)中,类似地,她只能向东走。
对于输入,石头 i位于坐标为X_i,Y_i的位置,(-1,000,000,000<= X_i <= 1,000,000,000; -1,000,000,000 <= Y_i <= 1,000,000,000),没有任何两块石头位于同一个位置,Bessie从Bx,By的位置出发(出发点一定与某个石头相邻),Bessie的目标位置是Gx,Gy(-1,000,000,000 <= Gx <= 1,000,000,000; -1,000,000,000 <= Gy <=1,000,000,000).
Bessie 并不介意长时间滑冰,但是,不停地推石头依靠反作用力前进很累。FJ 非常关心Bessie的健康,因此他希望知道Bessie最少要推多少次石头才能到达终点。
题目大意
奶牛要从起点到终点,每次要在某一个石头附近朝上下左右的某一个方向走到另外的一个石头附近。求拐弯方向-1。
Solution
广搜是十分容易想到的,我们需要根据每一个节点进行扩展。
这一道题的难点就是如何找到某一个坐标上下左右离它最近的石头:
- 上:在y坐标相等,且小于当前x坐标的情况下,有x坐标最大所对应的点。
- 下:在y坐标相等,且大于当前x坐标的情况下,有x坐标最小所对应的点。
- 左:在x坐标相等,且小于当前y坐标的情况下,有y坐标最大所对应的点。
- 右:在x坐标相等,且大于当前y坐标的情况下,有y坐标最小所对应的点。
这需要二分查找,为了方便计算我们可以使用STL的set和map来解决。
由于坐标比较大,我们可以使用map来建立映射关系,对于行,定义一份集合set来存储这一行的所有纵坐标;同理,对于每一列,存储所有的横坐标;然后我们在找的时候,直接在set上进行二分查找即可。
在二分查找的时候注意判断是否有解。
代码比较长,但是思路还是很清晰的。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <cmath>
#include <limits.h>
#include <queue>
#define pos pair<int,int>
#define x first
#define y second
using namespace std;
int n,bx,by,gx,gy;
struct node
{
int x,y,v;
};
map < pos, bool > v;
map < int, set<int> > map_x,map_y;
set <int> ::iterator it;
pos no_ans;
queue< node > q;
pos find_up(pos now)
{
set <int> temp=map_y[now.y];
it=temp.lower_bound(now.x);
if (it==temp.begin()) return no_ans;
it--;
if (abs(*it-now.x)<=1) return no_ans;
return pos{*it+1,now.y};
}
//查找点now上面的点
//纵坐标相同 横坐标小于now_x
pos find_down(pos now)
{
set <int> temp=map_y[now.y];
it=temp.upper_bound(now.x);
if (it==temp.end()) return no_ans;
if (abs(*it-now.x)<=1) return no_ans;
return pos{*it-1,now.y};
}
//查找点now下面的点
//纵坐标相同 横坐标大于now_y
pos find_left(pos now)
{
set <int> temp=map_x[now.x];
it=temp.lower_bound(now.y);
if (it==temp.begin()) return no_ans;
it--;
if (abs(*it-now.y)<=1) return no_ans;
return pos{now.x,*it+1};
}
//查找点now左边的点
//横坐标相同 纵坐标小于now_y
pos find_right(pos now)
{
set <int> temp=map_x[now.x];
it=temp.upper_bound(now.y);
if (it==temp.end()) return no_ans;
if (abs(*it-now.y)<=1) return no_ans;
return pos{now.x,*it-1};
}
//查找点now右边的点
//纵坐标相同 横坐标大于now_x
int main(void)
{
freopen("ice..in","r",stdin);
freopen("ice..out","w",stdout);
cin>>n>>bx>>by>>gx>>gy;
for (int i=1;i<=n;++i)
{
int x,y;
cin>>x>>y;
map_x[x].insert(y);
//map_x表示第x行对应的所有y坐标
map_y[y].insert(x);
//map_y表示第y行对应的所有x坐标
}
no_ans=pos{INT_MAX,-INT_MAX};//无解状态
q.push(node{bx,by,0});
pos st;
st=pos{bx,by};
v[st]=1;
while (q.size())
{
node top=q.front();
q.pop();
if (top.x==gx && top.y==gy)
return cout<<top.v,0;
//到达终点
pos now;
//初始化和边界
now=find_up(pos{top.x,top.y});
if (!v[now] && now!=no_ans)
{
v[now]^=1;
q.push(node{now.x,now.y,top.v+1});
}
//向上搜索
now=find_down(pos{top.x,top.y});
if (!v[now] && now!=no_ans)
{
v[now]^=1;
q.push(node{now.x,now.y,top.v+1});
}
//向下搜索
now=find_left(pos{top.x,top.y});
if (!v[now] && now!=no_ans)
{
v[now]^=1;
q.push(node{now.x,now.y,top.v+1});
}
//向左搜索
now=find_right(pos{top.x,top.y});
if (!v[now] && now!=no_ans)
{
v[now]^=1;
q.push(node{now.x,now.y,top.v+1});
}
//向右搜索
}
return 0;
}