[复杂模拟] UVa10366 龙头滴水(需要一定的推理能力)

题目

这里写图片描述

思路

(思路来自,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什么啊。。。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值