【stl && 题解】 ice

题目传送门

题目描述:

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)中,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最少要推多少次石头才能到达终点。


分析:

这题的最主要思路就是找到每一行每一列的石头个数,只要能找到石头,我们就能确定滑动后到达的位置。那么,这个时候,我们就可以写12个二分来解决这个问题(当然是假的。。),不过12个二分代码量有点大,我们考虑如何改进。

其实可以用 s e t set m a p map 优化

s e t set 是将每一行所对应石头的坐标进行存储
m a p map 是将每一个set所对应的步数记录下来

其实就是一个 m a p map s e t set 的过程

我们用了stl之后,二分的内容便可以省略,因为stl之中是自带二分的。

  • 如何在 s e t set m a p map 中进行二分查找呢?
    1、 对诸如set、map这种关键字唯一的集合而言,lower_bound、upper_bound返回迭代器是相同,关键字val在集合中不存在,二者返回结果一样,都是按照集合实例化时给定的Compare比较,不在val之前的第一个元素(亦即之后或者等于,如果按照默认的比较类型less,函数返回的是≥val的最小的元素);
  1. 如果关键在val在集合中存在,lower_bound返回val关键字本身的迭代器,upper_bound返回关键字val下一个元素迭代器。
  2. 按照关键字后面一个关键字在集合中出现的次数可分为:关键字val出现在集合中,但是是唯一的,这种情况和set、map情况类似;
  3. 关键字val出现在集合中,出现多次,这种情况下lower_bound返回第一个出现关键字val对应的迭代器,upper_bound返回位于关键字val对应位置后第一个不是val的位置的迭代器;关键字val不在集合中,这种情况下与set、map一致。

综合一下:lower_bound、upper_bound函数不管在什么情况下,以下条件均成立:
Iterator(val)≤Iterator(lower_bound)≤Iterator(upper_bound)
也就是lower_bound、upper_bound构成的上下限的区间总是表示一个有效的迭代器区间(equal_range返回值),该迭代区间的长度表示关键字val在集合中出现的次数。

好,下面开始正式分析:
因为题目给的坐标大小非常大,但是石头却最多只有20000个,所以我们可以从石头入手。
用map建立一个映射关系,来进行离散化石头的坐标即可。

而set中的集合是默认从小到大排序,所以这样也复合了二分查找的性质,我们也可以用系统自带的二分查找来查询最近的石头即可。

  • 如何寻找石头的位置??
    这里给一个往上查找的例子:
node go_up(node k){
    set<int> y=rock_x[k.fi];//找到相应的列
    it=y.upper_bound (k.se) ;//找到石头
    if (it==y.end()||(*it)-k.se<=1) return b;//不符合情况
    return node(k.fi,(*it)-1);//返回
}

bfs其实就是裸的bfs,主要是查找麻烦。


Code

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
typedef pair<int , int > node; 
int n;
map < int , set < int > > rock_x;
map < int , set < int > > rock_y;
queue < node > q;
map < node , int > dis;
node b,g;
set < int > :: iterator it;//表示是复制的来的 
node go_up(node k){
    set<int> y=rock_x[k.fi];
    it=y.upper_bound (k.se) ;
    if (it==y.end()||(*it)-k.se<=1) return b;
    return node(k.fi,(*it)-1);
}//往上查找
node go_down(node k){
    set<int> y=rock_x[k.fi];
    it=y.upper_bound (k.se) ;
    if (it==y.begin()||(k.se-(*(--it))<=1)) return b;
    return node(k.fi,(*it)+1);
}//往下查找
node go_left(node k){
    set<int> x=rock_y[k.se];
    it=x.upper_bound (k.fi) ;
    if (it==x.begin()||(k.fi-(*(--it))<=1)) return b;
    return node((*it)+1,k.se);
}//往左查找
node go_right(node k){
    set<int> x=rock_y[k.se];
    it=x.upper_bound (k.fi) ;
    if (it==x.end()||((*it)-k.fi<=1)) return b;
    return node((*it)-1,k.se);
}//往右查找
int main(){
	freopen("ice..in","r",stdin);
	freopen("ice..out","w",stdout);
	scanf("%d %d %d %d %d",&n,&b.fi,&b.se,&g.fi,&g.se);
    for (int i=1,x,y;i<=n;i++)
      scanf("%d %d",&x,&y),rock_x[x].insert(y),rock_y[y].insert(x);//建立每一个石头的行列的索引
    q.push(b);
    dis[b]=0;
    while (!q.empty()){
	    node x=q.front();
	    q.pop();
	    node xx;
	    xx=go_up(x);
	    if (xx!=b&&!dis[xx]) q.push(xx),dis[xx]=dis[x]+1;
	    xx=go_down(x);
	    if (xx!=b&&!dis[xx]) q.push(xx),dis[xx]=dis[x]+1;
	    xx=go_left(x);
	    if (xx!=b&&!dis[xx]) q.push(xx),dis[xx]=dis[x]+1;
	    xx=go_right(x);
	    if (xx!=b&&!dis[xx]) q.push(xx),dis[xx]=dis[x]+1;
	    if (dis[g]) break;
	}//bfs查找过程
	printf("%d",dis[g]);//输出
	fclose(stdin);
	fclose(stdout);
	return 0;
}

请大家支持我

猜你喜欢

转载自blog.csdn.net/huang_ke_hai/article/details/88913611
ice
STL