土地积水

来源:互联网 发布:阿里云学生认证账号 编辑:程序博客网 时间:2024/04/28 03:36
 

土地积水

分类: 算法 230人阅读 评论(2) 收藏 举报

http://www.byvoid.com/blog/poi-1999-wod/zh-hans/

有一块矩形土地被划分成 N×M 个正方形小块,每块是一平方米。这些小块高低不平,
每一小块地都有自己的高度H(i, j)米。水可以由任意一块地流向周围四个方向的四块地中,
但不能直接流入对角相连的小块中。一场大雨后,许多低洼地方都积存了不少降水,求出它最多能积存多少立方米的降水么?

根据木桶原理,水位的高低取决于最低的边界。我们可以从最低的边界入手,向内灌水,使水面于边界齐平。如果灌到了新的边界,而且不低于最低的边界,则这个点一定是不能被灌水的。

可以想象成一个深度搜索的过程,我们从最低边界开始灌水,遇到比最低边界低的,它的水平面顶多就是最低边界,直到遇到一个边界比最低边界高的,将高边界放入优先队列中。

每次取边界最小值向内灌水,于是可以用一个最小堆来维护高度。

算法流程如下:

  1. 把所有的边界上的点加入堆,且标记为已使用过。
  2. 取出高度最小的点(x,y),枚举与(x,y)相邻的未使用过的点(x’,y’),从(x’,y’)开始Floodfill,边界高度h=(x,y)的高度。
  3. 重复第二步,直到堆为空。
  • Floodfill(x,y)
  1. 标记(x,y)为已使用过。
  2. 如果(x,y)的高度>=h,则该点不能积水,加入堆并返回。
  3. 否则(x,y)的点的积水量为h-(x,y)的高度。
  4. 枚举与(x,y)相邻的未使用过的点(x’,y’),Floodfill(x’,y’)。
[cpp] view plaincopy
  1. /*  
  2.  * Problem: POI1999 wod 
  3.  * Author: Guo Jiabao 
  4.  * Time: 2008.12.16 21:44:50 
  5.  * State: Solved  
  6. */  
  7. #include <iostream>  
  8. #include <cstdio>  
  9. #include <cstdlib>  
  10. #include <cmath>  
  11. #include <cstring>  
  12.    
  13. using namespace std;  
  14.    
  15. const int MAX=101;  
  16. const int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};  
  17.    
  18. struct point  
  19. {  
  20.     int x,y;  
  21. };  
  22.    
  23. int N,M,All;  
  24. int Alt[MAX][MAX];  
  25. bool vid[MAX][MAX];  
  26. int heap_size;  
  27. point Heap[MAX*MAX];  
  28.    
  29. void heap_ins(int x,int y)  
  30. {  
  31.     int i;  
  32.     for (i=++heap_size;Alt[x][y]<Alt[Heap[i/2].x][Heap[i/2].y];i=i/2)  
  33.         Heap[i]=Heap[i/2];  
  34.     Heap[i].x=x; Heap[i].y=y;  
  35. }  
  36.    
  37. point heap_delmin()  
  38. {  
  39.     point R=Heap[1],M=Heap[heap_size--];  
  40.     int i,c;  
  41.     for (i=1;i*2<=heap_size;i=c)  
  42.     {  
  43.         c=i*2;  
  44.         if (c!=heap_size && Alt[Heap[c+1].x][Heap[c+1].y]<Alt[Heap[c].x][Heap[c].y])  
  45.             c++;  
  46.         if (Alt[M.x][M.y] > Alt[Heap[c].x][Heap[c].y])  
  47.             Heap[i]=Heap[c];  
  48.         else  
  49.             break;  
  50.     }  
  51.     Heap[i]=M;  
  52.     return R;  
  53. }  
  54.    
  55. void init()  
  56. {  
  57.     freopen("wod.in","r",stdin);  
  58.     freopen("wod.out","w",stdout);  
  59.     scanf("%d%d",&N,&M);  
  60.     for (int i=1;i<=N;i++)  
  61.         for (int j=1;j<=M;j++)  
  62.             scanf("%d",&Alt[i][j]);  
  63.     Alt[0][0]=-0x7FFFFFFF;  
  64.     Heap[heap_size=0].x=Heap[0].y=0;  
  65. }  
  66.    
  67. inline bool inrange(point A)  
  68. {  
  69.     return A.x>=1 && A.x<=N && A.y>=1 && A.y<=M;  
  70. }  
  71.    
  72. void floodfill(point A,int h)  
  73. {  
  74.     point B;  
  75.     vid[A.x][A.y]=true;  
  76.     if (Alt[A.x][A.y]>=h)  
  77.         heap_ins(A.x,A.y);  
  78.     else  
  79.     {  
  80.         All+=h-Alt[A.x][A.y];  
  81.         for (int i=0;i<4;i++)  
  82.         {  
  83.             B.x=A.x+dx[i]; B.y=A.y+dy[i];  
  84.             if (inrange(B) && !vid[B.x][B.y])  
  85.                 floodfill(B,h);  
  86.         }  
  87.     }  
  88. }  
  89.    
  90. void solve()  
  91. {  
  92.     int i,j;  
  93.     point A,B;  
  94.     for (i=1;i<=N;i++)  
  95.     {  
  96.         heap_ins(i,1);  
  97.         heap_ins(i,M);  
  98.         vid[i][1]=vid[i][M]=true;  
  99.     }  
  100.     for (i=2;i<=M-1;i++)  
  101.     {  
  102.         heap_ins(1,i);  
  103.         heap_ins(N,i);  
  104.         vid[1][i]=vid[N][i]=true;  
  105.     }  
  106.     while (heap_size)  
  107.     {  
  108.         A=heap_delmin();  
  109.         for (i=0;i<4;i++)  
  110.         {  
  111.             B.x=A.x+dx[i]; B.y=A.y+dy[i];  
  112.             if (inrange(B) && !vid[B.x][B.y])  
  113.                 floodfill(B,Alt[A.x][A.y]);  
  114.         }  
  115.     }  
  116. }  
  117.    
  118. int main()  
  119. {  
  120.     init();  
  121.     solve();  
  122.     printf("%d",All);  
  123.     return 0;  
  124. }  

还有一种方法,还是比较精巧的。

先考虑我们的土地是一维的,首先定义两个数字,left,right

left[i]记录的是从0~i-1最高的高度,right[i]记录的是从n~i+1最高的高度

那么i的水平面高度是,min(left[i], right[i]) - h[i]

https://github.com/codingtest/interview/blob/master/code2.cpp

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <vector>  
  3. #include <string>  
  4. #include <vector>  
  5. #include <list>  
  6. #include <map>  
  7. #include <set>  
  8. #include <queue>  
  9. #include <deque>  
  10. #include <stack>  
  11. #include <bitset>  
  12. #include <algorithm>  
  13. #include <functional>  
  14. #include <numeric>  
  15. #include <utility>  
  16. #include <sstream>  
  17. #include <iostream>  
  18. #include <iomanip>  
  19. #include <cstdio>  
  20. #include <cmath>  
  21. #include <cstdlib>  
  22. #include <ctime>  
  23.   
  24. using namespace std;  
  25.   
  26. //有一块矩形土地被划分成 N×M 个正方形小块,每块是一平方米。这些小块高低不平,  
  27. //每一小块地都有自己的高度H(i, j)米。水可以由任意一块地流向周围四个方向的四块地中,  
  28. //但不能直接流入对角相连的小块中。一场大雨后,许多低洼地方都积存了不少降水,求出它最多能积存多少立方米的降水么?  
  29. int trap(int* a, int n)  
  30. {  
  31.     if ( a == NULL || n == 0 )  
  32.         return 0;  
  33.     int* left = new int[n];  
  34.     if ( left == NULL )  
  35.            return 0;  
  36.      int* right = new int[n];  
  37.     if ( right == NULL )  
  38.            return 0;  
  39.   
  40.     int maxL = 0;  
  41.     for ( int i = 0 ; i < n-1; i++ )  
  42.     {  
  43.         left[i] = maxL;  
  44.         maxL = max(maxL, a[i]);  
  45.     }  
  46.   
  47.     int maxR = 0;  
  48.     for ( int i = n-1; i >= 0; i-- )  
  49.     {  
  50.         right[i] = maxR;  
  51.         maxR = max(maxR, a[i]);  
  52.     }  
  53.   
  54.     int res = 0;  
  55.     for ( int i = 0 ; i < n-1 ;i++)  
  56.     {  
  57.         int v = min(left[i], right[i]) - a[i];  
  58.         if ( v > 0 )  
  59.             res += v;  
  60.     }  
  61.     delete[] left;  
  62.     delete[] right;  
  63.     return res;  
  64. }  
原创粉丝点击