2 条题解

  • 0
    @ 2026-9-29 11:29:59

    题目传送门

    题目描述:

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

    其实可以用 setset 和 mapmap 优化

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

    其实就是一个mapmap 套 setset 的过程

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

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

    请大家支持我

    • 0
      @ 2025-10-8 16:58:15
      #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(){
      	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]);//输出
      	return 0;
      }
      
      • 1

      【宽搜+set二分】冰上[USACO10FEB] Cows on Ice G

      信息

      ID
      1692
      时间
      1000ms
      内存
      128MiB
      难度
      9
      标签
      递交数
      7
      已通过
      6
      上传者