2 条题解

  • 0
    @ 2026-9-26 20:40:55

    写的时候,总感觉是管理员设错了。

    这是一道非常非常普通的bfs。

    接下来让我们来仔细的分析一下。


    First

    先看题干,题目中的意思是输入一张 n * n 的地图,让我们输出地图中的山峰和山谷。

    题中的山峰和山谷内的任意一块一定是相平的。 那么,这题的目的也就清楚了。这是一道搜地图中的连通块的模板题。

    不过要注意:

    1.每个连通块不一定是山峰或山谷,也可能什么也不是。我们要及时判断。
    
    2.每一次扩展需要探索的点时,要注意为8个方向,代码实现时要小心,不能思维定式。
    

    对于 1. 我们的解决办法是在搜连通块时,如果要扩展的地方低于 或 高于当前的位置时,分别用变量记录,但并不放入搜索队列中。

    对于 2. 我们的解决办法就轻松多了,只要在定义方向数组时横纵坐标都为8就行了


    好了,

    我们已经将题干中的信息都提取出来了,算法也清晰了,接下来就是代码的实现问题了。

    在代码中的注意点也会标出

    #include<bits/stdc++.h>
    using namespace std;
    int n;
    int a[10001][10001];
    int dep[10001][10001];//这个数组主要表明地图中已经走过的位置 对代码的优化有很大帮助 但是要注意 在比赛中前往不要定义这么大,会爆的(~~我已经有深刻的体会了~~)
    struct xi{
    	int x , y;
    }c[1000001];//这里的队列使用一个struct来表示当前要搜的位置的横纵坐标
    int h = 1 , t = 1;
    /*这里的队列是我手打的习惯了STL的小伙伴可能会不习惯,
    但是我还是要说一声 head 为 t , tail 为 h ,千万不要看错*/
    int ans1 = 0 , ans2 = 0;//表示山峰 和 山谷的数量 
    int ax[8] = {-1 , -1 , -1 , 0 , 1 , 1 , 1 , 0};
    int ay[8] = {-1 , 0 , 1 , 1 , 1 , 0 , -1 , -1};//方向数组要小心定义 , 弄清楚各个方向
    void bfs(int x , int y){
    	int xa = 0 , ya = 0;//表示当前的平地的周围是否有低地 或 高地
    	h ++;//当上一次while退出时 , h == t ,所以此时要++
    	c[t].x = x , c[t].y = y;//把起始位置附入队列的头
    	do{
    //		cout << c[t].x << " " << c[t].y << endl; 
    //		dep[c[t].x][c[t].y] = 1;检查代码 可以忽略 但是大家在比赛中的调试代码千万不要留下
    		for(int i = 0;i < 8;i ++){//搜8个方向
    			int xx = c[t].x + ax[i] , yy = c[t].y + ay[i]; 
    			if(xx <= 0 || xx > n || yy <= 0 || yy > n)continue;//越界跳过
    			if(a[xx][yy] < a[c[t].x][c[t].y]){//表示目标位置低于当前位置 即 当前的平地周围有低地 
    				xa = 1;
    			}
    			else if(a[xx][yy] > a[c[t].x][c[t].y]){//表示目标位置高于当前位置 即 当前的平地周围有高地 
    				ya = 1;
    			}
    			if(dep[xx][yy])continue;//这里要注意 判断是否搜过要在判断目标位置之后。
    //不懂得小伙伴可以调换一下,测试一下 一定会出错
    			if(a[xx][yy] == a[c[t].x][c[t].y]){//表示目标位置等于当前位置 即 目标位置是当前的平地的一部分可以入队 
    				c[h].x = xx;
    				c[h].y = yy;
    				dep[c[h].x][c[h].y] = 1;
    				h ++;//入队操作
    			}
    		}
    //		cout << " 1 " << endl;
    		t ++;
    	}while(h > t);
    	if(xa == 1){
    		if(ya == 0)//表示当前的平地周围都较低 当前的平地为为山峰 
    		{
    			ans1 ++;
    		}
    	}
    	else if(ya == 1){
    //表示当前的平地周围都较高 当前的平地为山谷 
    			ans2 ++;
    	}//这就是解决注意 1. 的方法
    	return ;
    }
    void print(){
    /*这里要说一声 当整个地图都为同一高度时 
    它既是一个山峰 也是一个山谷 
    所以在输出是要把整张地图都遍历一遍*/
    	if(ans1 || ans2){
    		cout << ans1 << " " << ans2 << endl;
    		return ;
    	}
    	int x = 0;
    	for(int i = 2;i <= n;i ++)
    	 for(int j = 2;i <= n;i ++){
    	 	if(a[i][j] != a[i - 1][j])
    	 	x = 1;
    	 }
    	for(int i = 2;i <= n;i ++)
    	if(a[i][1] != a[i - 1][1])
    	x = 1;
    	if(x){
    		cout << ans1 << " " << ans2 << endl;
    		return ;
    	}
    	cout << 1 << " " << 1 << endl;
    	return ;
    }
    int main(){
    //	freopen("hill.in" , "r" , stdin);
    //	freopen("hill.out" , "w" , sdtout);
    //考试的时候 freopen一定不要忘记取消注释
    	cin >> n;
    	for(int i = 1;i <= n;i ++)
    	 for(int j = 1;j <= n;j ++)
    	 cin >> a[i][j];
    	/*	
    	for(int i = 1;i <= n;i ++){
    		for(int j = 1;j <= n;j ++)
    		cout << a[i][j] << " ";
    		cout << endl;
    	}
    	*///个人习惯
        for(int i = 1;i <= n;i ++)
    	 for(int j = 1;j <= n;j ++)
    	 if(!dep[i][j]){
    //	 cout << i << " " << j << endl;
    	 	dep[i][j] = 1;
    	 	bfs(i , j);
    	 }
    	print(); 
    	return 0;
    } 
    

    以上为完整的代码。 (如果你还没看懂,就再看一遍吧!)


    END

    (写的不好,大佬勿喷)
    • 0
      @ 2025-10-8 17:02:57
      #include<bits/stdc++.h>
      using namespace std;
      const int N=1010;
      int n,h[N][N],w;//w==1谷 , w==2峰 
      bool vis[N][N];
      int dx[8]={-1,-1,-1,0,0,1,1,1};
      int dy[8]={-1,0,1,-1,1,-1,0,1};
      void dfs(int x,int y)
      {
      	vis[x][y]=1; //标记当前点已访问
          for(int i=0;i<8;i++)//搜索与当前点相邻的8个点
          {
              int xx=x+dx[i],yy=y+dy[i];
              if(!(xx>=1&&xx<=n&&yy>=1&&yy<=n))continue;
              if(h[xx][yy]==h[x][y]&&vis[xx][yy]==0) dfs(xx,yy);
              else if( (h[xx][yy]<h[x][y]&&w==1) ||
      		         (h[xx][yy]>h[x][y]&&w==2)  ) //周围既有比它高的又有比它低的 则它什么也不是,为-1
      					w=-1;
      
              else if(w==0)//没确定是是峰还是谷,为0。搜到与当前搜索高度不同的点 暂且标记当前搜索区域是峰还是谷
              {
                  if(h[xx][yy]<h[x][y])w=2;
                  if(h[xx][yy]>h[x][y])w=1;
              }
          }
      }
      int main()
      {
          scanf("%d",&n);    
          bool bk=1;int v=-1;
      	for(int i=1;i<=n;i++)
              for(int j=1;j<=n;j++)
      		{
                  scanf("%d",&h[i][j]);
      			if(v==-1)v=h[i][j];
      			if(h[i][j]!=v)bk=0; //如果有高度不相同的点
      		}
      	if(bk){printf("1 1\n");return 0;}	
          
      	memset(vis,0,sizeof(vis));
      	int a=0,b=0; //a表示山峰个数,b表示山谷个数
          for(int i=1;i<=n;i++)
              for(int j=1;j<=n;j++)
                  if(vis[i][j]==0)//找到一个没搜过的点开始搜索
                  {
      				w=0; //w=0表示当前点还没确定是山峰还是山谷
                      dfs(i,j);
                      if(w==1)a++;
                      else if(w==2)b++;
                  }
          printf("%d %d\n",b,a);
          return 0;
      }
      
      • 1

      [POI 2007] GRZ-Ridges and Valleys山峰和山谷

      信息

      ID
      2755
      时间
      2000ms
      内存
      64MiB
      难度
      6
      标签
      递交数
      22
      已通过
      11
      上传者