题目
思路
(思路来自,aoapc习题选解,陈锋)
1.基本工具:
如果有两个挡板X和Y,X不高于Y,并且X和Y之间没有比Y还高的挡板,那么X左边来的水要流到Y,在接触Y之前,会形成一个阶梯形状。也就是说,从X流到Y所需要的时间就是阶梯下方的面积。
2.回到题目,考虑X=0的左右两边最高的挡板高度LH、RH,以及其位置(LHi, RHi)。
3.如果LH == RH
那么说明水从两个边缘都会溢出来,于是就分别计算水从LHi到左边缘溢出所需时间lt,从RHi到右边缘溢出所需时间rt。因为两边同时在流,对于一边,流水效率就是X=0处流水效率的一半。所有最后的结果就是,水把LHi-RHi这个区间的矩形装满水加上min(lt,rt)*2,总的面积所需要的时间。
4. 如果 LH < RH。假设Ti是X=0右边第一个高度>=LH的。
那么水一定是先从左边缘溢出。
而且水流分两部分,一部分从Ti流到Ti右边第一个高度大于LH的挡板那,假设所需水量为rt,另一部分从LHi向左溢出到左边缘,假设所需水量为lt。
(1).如果lt>rt,那么所求结果就是,LHi和Ti之间高度为LH的矩形对应的水量加上lt和rt。
(2).如果lt
代码
// 代码来自aoapc习题选解,陈锋,侵删
#include <cassert>
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
#include <set>
using namespace std;
const int MAXD = 1000+4;
vector<int> H;
int L, R, LHs[MAXD], RHs[MAXD], LH, RH, LHi, RHi;
int solve() {
if(LH == RH) {
int lt = 0, rt = 0;
for(int i = L, h = LHs[L]; i > LHi; i--) // 从左边最高点溢出之后到边缘需要的水量
lt += h, h = max(h, LHs[i-1]);
for(int i = R, h = RHs[R]; i > RHi; i--) // 从右边最高点溢出之后到边缘需要的水量
rt += h, h = max(h, RHs[i-1]);
return (LHi + RHi + 1) * LH * 2 + min(lt, rt) * 2 * 2;
}
int T = min(LH, RH), LTi = 0, RTi = 0; // 左右两边第一个高度 >= T的
while(LTi < L && LHs[LTi] < T) LTi++;
while(RTi < R && RHs[RTi] < T) RTi++;
int lt = 0, rt = 0, t;
if(LH < RH) { // 从左边溢出
for(int i = L, h = LHs[L]; i > LHi; i--)
lt += h, h = max(LHs[i-1], h);
for(int i = RTi, h = T; RHs[i] <= T; i++)
rt += h, h = max(RHs[i+1], h);
t = lt > rt ? (lt+rt) : 2*lt;
}
if(LH > RH) { // 从右边溢出
for(int i = R, h = RHs[R]; i > RHi; i--)
rt += h, h = max(RHs[i-1], h);
for(int i = LTi, h = T; LHs[i] <= T; i++)
lt += h, h = max(h, LHs[i+1]);
t = rt > lt ? (rt+lt) : 2*rt;
}
return t*2 + (RTi + LTi + 1) * T * 2;
}
int main() {
int leftx, rightx;
while(cin>>leftx>>rightx && leftx && rightx) {
LH = RH = 0;
L = (-leftx)/2, R = rightx/2;
for(int i = leftx; i < 0; i += 2) {
int xi = (-i)/2; cin>>LHs[xi];
if(LH <= LHs[xi]) LH = LHs[xi], LHi = xi; // 左边离0最近的最高点,注意加上等号
}
for(int i = 1; i <= rightx; i += 2) {
int xi = i/2; cin>>RHs[xi];
if(RH < RHs[xi]) RH = RHs[xi], RHi = xi; // 右边离0最近的最高点及其位置
}
cout<<solve()<<endl;
}
return 0;
}
收获
1.本题很明智的一点,在于先明确了一个基本工具,也就是最普遍情况下解的求法。随后再根据一些特殊情况,分别把求解转换为用基本工具的几个步骤,从而使求解简单明了。
(我在bb什么啊。。。)