1 条题解

  • 0
    @ 2025-8-24 21:44:04

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 鹭天
    **

    搬运于2025-08-24 21:44:04,当前版本为作者最后更新于2019-03-30 15:46:40,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目传送门

    题目描述:

    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个二分代码量有点大,我们考虑如何改进。

    其实可以用 setsetmapmap 优化

    setset是将每一行所对应石头的坐标进行存储 mapmap是将每一个set所对应的步数记录下来

    其实就是一个mapmapsetset 的过程

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

    • 如何在 setsetmapmap 中进行二分查找呢? 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;
    }
    
    

    请大家支持我

    • 1

    信息

    ID
    2046
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者