来源:http://poj.org/problem?id=2922
题意:给出一张N x N的地图,找出一条从左上角到右下角的路径,使得该路径上的【最大值】- 【最小值】结果最小,输出该结果。
分析:该地图规模最大为100 x 100,如果直接暴力搜索,我们必须对每条可能的路径进行搜索,才能得出正确的结果,时间复杂度为O(410000),一定会超时。所以,我们只能寻求其他的切入点。
我们试着这样去分析问题,我们很难直接地在搜索中使用剪枝,而我们最终要得到的是一个差值,这个差值的区间在【0,200】,由路径中的最大值与最小值得出。把问题分析到这里,我们考虑是否可以通过枚举差值,通过这个差值去找到对应的上界与下界的集合。判断题目给的图是否满足我们所枚举的条件,通过这样的方式得到的一个可满足我们刚才提到的条件的最小差值,就是我们需要的答案。同时,我们也解决了原图难以剪枝的问题,判断该图是否满足条件,只要把最大值/最小值越过上下界的路给剪掉即可,搜一次图的成本为O(n2)
考虑到这里还不够,这道题依然会超时,因为200个差值一一枚举还是太多了,我们要想办法优化某些流程。刚好,我们眼前的这个区间就很好下手,我们由此不难想到二分法,通过二分去找一个差值,我们可以将差值的枚举量降为log(200),到这里,问题就已经解决了。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 5 using namespace std; 6 7 int Mx[4] = {1,0,-1,0}; 8 int My[4] = {0,1,0,-1}; 9 10 struct Node { 11 int x,y,min,max; 12 13 Node(int x, int y, int min, int max):x(x),y(y),min(min),max(max){} 14 }; 15 16 int n; 17 int Maze[100][100]; 18 bool vis[100][100]; 19 20 bool isAvailable(int x) { 21 for (int lower=0;lower+x<=200;lower++) { 22 memset(vis,0,sizeof(vis)); 23 queue<Node> Q; 24 Q.push(Node(0,0,Maze[0][0],Maze[0][0])); 25 if (Maze[0][0] < lower || Maze[0][0] > lower+x) 26 continue; 27 vis[0][0] = 1; 28 while (!Q.empty()) { 29 Node p = Q.front(); 30 Q.pop(); 31 for (int i=0;i<4;i++) { 32 int xx = p.x + Mx[i]; 33 int yy = p.y + My[i]; 34 35 if (xx < 0 || yy < 0 || xx == n || yy == n || vis[xx][yy]) 36 continue; 37 if (Maze[xx][yy] < lower || Maze[xx][yy] > lower+x) 38 continue; 39 if (xx == n-1 && yy == n-1) 40 return true; 41 vis[xx][yy] = 1; 42 Q.push(Node(xx,yy,min(p.min,Maze[xx][yy]),max(p.max,Maze[xx][yy]))); 43 } 44 } 45 } 46 return false; 47 } 48 49 int main() { 50 int t,cas = 1; 51 scanf("%d",&t); 52 53 while (t--) { 54 scanf("%d",&n); 55 for (int i=0;i<n;i++) { 56 for (int j=0;j<n;j++) { 57 scanf("%d",&Maze[i][j]); 58 } 59 } 60 int L = 0, R = 200; 61 while (L < R) { 62 int mid = (L+R)/2; 63 if (isAvailable(mid)) { 64 R = mid; 65 } else { 66 L = mid + 1; 67 } 68 } 69 printf("Scenario #%d:\n%d\n\n",cas++,R); 70 } 71 72 return 0; 73 }