http://www.byvoid.com/blog/poi-1999-wod/zh-hans/
有一块矩形土地被划分成 N×M 个正方形小块,每块是一平方米。这些小块高低不平,每一小块地都有自己的高度H(i, j)米。水可以由任意一块地流向周围四个方向的四块地中,但不能直接流入对角相连的小块中。一场大雨后,许多低洼地方都积存了不少降水,求出它最多能积存多少立方米的降水么?
根据木桶原理,水位的高低取决于最低的边界。我们可以从最低的边界入手,向内灌水,使水面于边界齐平。如果灌到了新的边界,而且不低于最低的边界,则这个点一定是不能被灌水的。
可以想象成一个深度搜索的过程,我们从最低边界开始灌水,遇到比最低边界低的,它的水平面顶多就是最低边界,直到遇到一个边界比最低边界高的,将高边界放入优先队列中。
每次取边界最小值向内灌水,于是可以用一个最小堆来维护高度。
算法流程如下:
/* * Problem: POI1999 wod * Author: Guo Jiabao * Time: 2008.12.16 21:44:50 * State: Solved */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> using namespace std; const int MAX=101; const int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0}; struct point { int x,y; }; int N,M,All; int Alt[MAX][MAX]; bool vid[MAX][MAX]; int heap_size; point Heap[MAX*MAX]; void heap_ins(int x,int y) { int i; for (i=++heap_size;Alt[x][y]<Alt[Heap[i/2].x][Heap[i/2].y];i=i/2) Heap[i]=Heap[i/2]; Heap[i].x=x; Heap[i].y=y; } point heap_delmin() { point R=Heap[1],M=Heap[heap_size--]; int i,c; for (i=1;i*2<=heap_size;i=c) { c=i*2; if (c!=heap_size && Alt[Heap[c+1].x][Heap[c+1].y]<Alt[Heap[c].x][Heap[c].y]) c++; if (Alt[M.x][M.y] > Alt[Heap[c].x][Heap[c].y]) Heap[i]=Heap[c]; else break; } Heap[i]=M; return R; } void init() { freopen("wod.in","r",stdin); freopen("wod.out","w",stdout); scanf("%d%d",&N,&M); for (int i=1;i<=N;i++) for (int j=1;j<=M;j++) scanf("%d",&Alt[i][j]); Alt[0][0]=-0x7FFFFFFF; Heap[heap_size=0].x=Heap[0].y=0; } inline bool inrange(point A) { return A.x>=1 && A.x<=N && A.y>=1 && A.y<=M; } void floodfill(point A,int h) { point B; vid[A.x][A.y]=true; if (Alt[A.x][A.y]>=h) heap_ins(A.x,A.y); else { All+=h-Alt[A.x][A.y]; for (int i=0;i<4;i++) { B.x=A.x+dx[i]; B.y=A.y+dy[i]; if (inrange(B) && !vid[B.x][B.y]) floodfill(B,h); } } } void solve() { int i,j; point A,B; for (i=1;i<=N;i++) { heap_ins(i,1); heap_ins(i,M); vid[i][1]=vid[i][M]=true; } for (i=2;i<=M-1;i++) { heap_ins(1,i); heap_ins(N,i); vid[1][i]=vid[N][i]=true; } while (heap_size) { A=heap_delmin(); for (i=0;i<4;i++) { B.x=A.x+dx[i]; B.y=A.y+dy[i]; if (inrange(B) && !vid[B.x][B.y]) floodfill(B,Alt[A.x][A.y]); } } } int main() { init(); solve(); printf("%d",All); return 0; }
先考虑我们的土地是一维的,首先定义两个数字,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
#include <stdio.h> #include <vector> #include <string> #include <vector> #include <list> #include <map> #include <set> #include <queue> #include <deque> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iostream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <ctime> using namespace std; //有一块矩形土地被划分成 N×M 个正方形小块,每块是一平方米。这些小块高低不平, //每一小块地都有自己的高度H(i, j)米。水可以由任意一块地流向周围四个方向的四块地中, //但不能直接流入对角相连的小块中。一场大雨后,许多低洼地方都积存了不少降水,求出它最多能积存多少立方米的降水么? int trap(int* a, int n) { if ( a == NULL || n == 0 ) return 0; int* left = new int[n]; if ( left == NULL ) return 0; int* right = new int[n]; if ( right == NULL ) return 0; int maxL = 0; for ( int i = 0 ; i < n-1; i++ ) { left[i] = maxL; maxL = max(maxL, a[i]); } int maxR = 0; for ( int i = n-1; i >= 0; i-- ) { right[i] = maxR; maxR = max(maxR, a[i]); } int res = 0; for ( int i = 0 ; i < n-1 ;i++) { int v = min(left[i], right[i]) - a[i]; if ( v > 0 ) res += v; } delete[] left; delete[] right; return res; }