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]重合时
蓝色和红色走到比黑色所指向的类的高度高或者相等时就停下,此时黑色就从红色(/蓝色)跳到当前蓝色(/红色)的位置
最后一张是发现忘记了画水,补上颜色为水。