题目
农夫约翰和贝西牛已经开始了其中一个“积极”的假期。他们整天都在山里散步,然后在一天结束时,他们厌倦了回到度假小屋。
由于攀爬需要大量能量并且已经疲惫,他们希望使用其最高和最低高度之间的差异最小的路径返回到机舱,无论路径有多长。帮助FJ找到这条易于移动的路径。
山的地图由N×N(2 <= N <= 100)整数高程矩阵给出(0 <=任意高程<= 110)FJ和Bessie当前位于左上角位置(第1行,列) 1)并且舱室位于右下方(第N行,第N列)。它们可以向右,向左,向顶部或向网格底部移动。他们不能在对角线上旅行。
输入
*第1行:单个整数,N
*行2…N + 1:每行包含N个整数,每个整数指定一个正方形的高度。第2行包含网格的第一行(顶部); 第3行包含第二行,依此类推。该行上的第一个数字对应于网格的第一个(左)列,依此类推。
产量
*第1行:一个整数,它是最佳路径上的最小高度差。
样本输入
5
1 1 3 6 8
1 2 2 5 5
4 4 0 3 3
8 0 2 3 4
4 3 0 2 1
样本输出
2
分析与解答
这题根滑雪那到一样,用普通的搜索根本没法写,因为广搜每个节点只访问一次,而这个并不是只找一条路。深搜的话找路径需要记录当前DFS路径上所遇到所有点的高度,有些路径中的某个点高度过高,一看就知道不需要走,但是深搜还是要走,所以时间上浪费
这题利用二分,我们二分的是个高度差,就是说当前高度差处于[low,up]这个区间的范围之中,对于这个区间[low,up]我们用BFS找,看看能不能找到一条从左上角到右下角的路,
我们bfs参数是数的高度,他们的高度差我们二分出来了,现在就不断枚举所有可能的起止高度进行遍历,比如高度差位mid,那么区间就是(low,low+mid)low从一递增到一百一.。只要有一个路径的数都在区间里面我们就return
bfs从(1,1)开始搜,先判断起点是不是在区间范围内,不是的话返回,是的话入队,然后只要队不空,取队首,然后上下左右移动,一般的bfs,只要是第一次出现,在这个区域内,就入队了,这个区间的bfs 是有选择的入队,只有属于我们规定的的区间的我们才让他入队,就这么一直如下去,如果碰到这个坐标是(n,n)就说明已经找到了属于这个区间的路径,返回true
注意两个if的顺序,四个方向能走的不一定在这个区间里
参考代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=105;
int map[maxn][maxn];
int vis[maxn][maxn];
int n;
int to[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
struct node{int x,y;
};int bfs(int l,int r)
{if(map[1][1]<l||map[1][1]>r) return 0;queue<node> q;node k;k.x=1;k.y=1;q.push(k);vis[1][1]=1;while(!q.empty()){node a,b;b=q.front();q.pop();int x=b.x;int y=b.y;for(int j=0;j<4;++j){int ax=x+to[j][0];int ay=y+to[j][1];if(ax>=1&&ax<=n&&ay>=1&&ay<=n&&!vis[ax][ay]){vis[ax][ay]=1;if(map[ax][ay]>=l&&map[ax][ay]<=r){if(ax==n&&ay==n) return true; a.x=ax;a.y=ay;q.push(a);}}}}return false;}int check(int d){for(int i=0;i+d<=110;i++){memset(vis,0,sizeof(vis));if(bfs(i,i+d)) return 1; }return false;}
int main()
{scanf("%d",&n);for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)scanf("%d",&map[i][j]);int l=0,r=110;while(r>l){int mid = (r+l)/2;if(check(mid)) r=mid;else l=mid+1;}printf("%d\n",r);return 0;
}
}
欣赏一下dfs
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100+5;
int n;
int map[maxn][maxn];
int vis[maxn][maxn];//判断当前节点时候被走过
int max_v,min_v;
int dr[]={-1,1,0,0};//上下左右
int dc[]={0,0,-1,1};
bool dfs(int r,int c,int low,int up)
{vis[r][c]=1; //错误,这里之前vis放到了if下面if(map[r][c]>up || map[r][c]<low) return false;if(r==n&&c==n) return true; //到达终点for(int dir=0;dir<4;dir++){int nr=r+dr[dir],nc=c+dc[dir];if(nr>=1&&nr<=n&&nc>=1&&nc<=n&&!vis[nr][nc])if(dfs(nr,nc,low,up)) return true; //错误,这里忘了返回true了}return false;
}
bool check(int d)
{for(int low=0;low+d<=200;low++){memset(vis,0,sizeof(vis)); //错误,这里memset放到了for上面if(dfs(1,1,low,low+d)) return true;}return false;
}
int main()
{int T; scanf("%d",&T);for(int kase=1;kase<=T;kase++){scanf("%d",&n);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&map[i][j]);int L=0,R=200;while(R>L){int mid=(R+L)>>1;if(check(mid)) R=mid;else L=mid+1;}//printf("Scenario #%d:\n%d\n\n",kase,R);printf("%d\n",R);}return 0;
}