广工决赛-积木积水

http://gdutcode.sinaapp.com/problem.php?cid=1039&pid=4

Description

现有一堆边长为1的已经放置好的积木,小明(对的,你没看错,的确是陪伴我们成长的那个小明)想知道当下雨天来时会有多少积水。小明又是如此地喜欢二次元,于是他把这个三维的现实问题简化成二维的问题。设雨量无穷、积木不透水、积木间无缝连接,问在这个二次元的世界里,已放置好的积木会有多少单位的积水量?

Input

第一行包含一个整数T(T≤100),表示接下来的测试样例个数。每个测试样例有两行组成:第一行包含一个整数N(N≤1e6),表示积木的列数;第二行包含N个整数Ai(Ai≤1e6),表示第i列积木的个数。

Output

每个样例输出一行,包含一个整数,为题目所求。

Sample Input

1

11

6 2 2 4 2 0 3 4 4 5 1

Sample Output

19

分析:

题意很简单就是问在能放水的地方有多少,而问题的关键就是哪个地方能放水。

首先,从给的图例可以简单看到,在两边都比中间高时,最高水位取决于最低的一边,这很好理解。

(下面讲如何确定/划分出两个边,将图分为多个区域)

但是,如果是两边的积木比中间低呢?如图一,还是不难只要将图分为两部分就可以了,像图二,则划分为四个,两个图有一个共同的解法就是,将最高的积木看为中心,然后向中心靠近,划分区域,将水求和。

划分的依据是,从两边出发,记录边的积木个数N,一直到遇到比N大的积木个数的边,这两个边就确定了一个区域,将这个区域内的水求出,以此直到遍历结束。

前面一直在讲中间高的情况,剩下就是两边比中间都高的情况,如果两边比中间高,则只要简单的将中间的水求和即可。

所有上面描述,可以放水的地方就是在确定两边(组成的区域)之后,(区域)中间任何一边与(区域)两边中最矮边的积木个数差的位置。

///看代码一般先看宏定义和全局变量,然后从main函数开始看

#include//输入输出,只学习过#include 的同学将cin转换为

//scanf,cout转换为printf

#include

#defineE_MAXLEN1000100//N(N≤1e6)题目的最大输入数据量

usingnamespacestd;

intE_LIST[E_MAXLEN];//根据题目定义的积木的数组(此题为E题,E_LIST)

intE_Find_Top(intn){//该函数将返回数组中积木最高的边的数组下标

inti;

intt = 0;

for(i = 1; i

t = (E_LIST[t] > E_LIST[i]) ? t : i;

}

returnt;

}

voidE_COUNT(intn){

inttopest = E_Find_Top(n);

inti;

longlongresult = 0;

//最高在两边是,只要一直累加差值就可以

if(topest == 0) {

//左侧出现最高

//从右往左

intt = E_LIST[n- 1];

for(i =n- 2; i > 0; i--){

if(E_LIST[i] > t){

t = E_LIST[i];

}

else{

result += (t - E_LIST[i]);

}

}

}

elseif(topest == (n- 1)) {

//右侧出现最高

//从左往右

intt = E_LIST[0];

n--;

for(i = 1; i

if(E_LIST[i] > t){

t = E_LIST[i];

}

else{

result += (t - E_LIST[i]);

}

}

}

else{//这里看不懂,就看下面的图解

//中间出现最高

//左右左右靠拢

intr[2] = {n- 1, 0 };//分别指向两个边界的数组下标

intc = 1;//表示当前的边界为哪一个

intt = E_LIST[0];//初始化下,c = 1,表示从左边开始遍历

//则此时边界的高度是E_LIST[0]

while(r[0] >= r[1]) {//一直遍历到两个边界的指向重合

r[c] += (c) ? 1 : -1;//如果c

= 1,则向右边进行遍历

//如果c = 0,则向左边进行遍历

//遍历直到找到一个边比当前的边界高或者直到左右边界重合

while(E_LIST[r[c]] = r[1]){

//将差值加起来

result += (t - E_LIST[r[c]]);

//根据c值,确定往左往右走

r[c] += (c) ? 1 : -1;

}

//跳到另一边开始遍历

c = 1 - c;

//如果此时另一边是最高边则跳回来

if(topest == r[c]) {

c = 1 - c;

}

//将当前确定好的边的积木记录下来,进行下一次遍历

t = E_LIST[r[c]];

}

}

cout << result << endl;

}

intmain(){

intT;//题目输入描述的测试组

cin >> T;

while(T) {

inti,

j,

result = 0;

memset(E_LIST, 0,sizeof(E_LIST));//将数组E_LIST中的所有数据置为0

cin >> i;

for(j = 0; j < i;j++) {

cin >> E_LIST[j];

}

if(i < 3) {//如果只有两个边,自然就没有放水的地方

cout <<"0\n"<< endl;

}

else{

E_COUNT(i);//计算水量

}

T--;

}

return0;

}

以下黑色为t = E_LIST[x],中的x的值

蓝色为r[1]的所在列红色为r[0]的所在列

黄色为r[0]与r[1]重合时

蓝色和红色走到比黑色所指向的类的高度高或者相等时就停下,此时黑色就从红色(/蓝色)跳到当前蓝色(/红色)的位置

最后一张是发现忘记了画水,补上颜色为水。

你可能感兴趣的:(广工决赛-积木积水)