CSU-ACM 1838: Water Pump(单调栈+前缀后缀和)

特记

专为某人写的题解,记住哟,要思考思考

题意

给你 n 个墙壁,墙壁中间是一些房子村庄,然后就是老天开始下雨了,将墙壁之间的空隙给填满了,一句话:就是发洪灾啦,然后呢,政府需要泄洪,于是在这些墙壁之间打通一个下水管,将这里的水给排出去,最后问打通哪里的下水管能够排出去的水最多,输出最终结果

解题思路

  1. 思路一
    如果当前我们选择i处开通下水管,那么最终的状态就是,左边的水都会满,右边的水都会满,而 i 处的水都会被放掉,那么遵循这个思路,我们先运用单调栈保存到当前位置距离i最近并且比 i 处的墙高或者最接近i高度的墙的位置在哪里,如下图:这里写图片描述
    然后从左边求一遍以 1 为起点以i为终点,这之间水填满时的水量为 dpl[i] ,同理从右边求一遍 dpr[i] ,最终的答案就是 =min{dpl[i]+dpr[i+1]}

    • 思路二
      i 处为中心,往左边找最高的墙,往右边找最高的墙,然后直接计算空出来的空间是多少即是排出去的水量(当然这方法会出现超时,因为每一次都要找最高的会导致遍历几乎整个墙,从而O(n2)的复杂度),如下图:这里写图片描述

    • 比较

      不用多想,我们可以非常明显的知道,思路二思路是非常清晰的,很明了,然而边界问题可能会有点坑爹,队友在这个上面坑了一个多小时依旧GG,而第一种思路则非常巧妙,前提得知道单调栈,单调栈其实非常好理解,不会的读者可以百度学习一下,分分钟钟的事情,然后就是这种方法基本不用考虑什么边界问题,直接就是左边加右边即可完成,属于第二种思路的逆向思维

      代码

      #include <cstdio>
      #include <vector>
      #include <cstring>
      #include <algorithm>
      
      using namespace std;
      
      const int MAXN = 1e5 + 5;
      const int INF = 0x3f3f3f3f;
      typedef long long LL;
      typedef pair<int, int> PII;
      vector<PII> w;
      int n, o[MAXN];
      int dpl[MAXN], dpr[MAXN];
      
      void solve(int *dp, int l, int r) {
          int d = l <= r ? 1 : -1;
          w.clear();
          for(int i = l; i != r; i += d) {
              PII in(0, l);
              //单调栈部分(可以用stack实现,也可以用其他,方法各异)
              while(w.size() && w.back().first <= o[i]) {
                  in = max(in, w.back());
                  w.pop_back();
              }
              if(w.size()) in = max(in, w.back());
              while(!w.size() || w.back().first > o[i]) {
                  w.push_back(PII(o[i], i));
              }
              //单调栈部分
              dp[i] = dp[in.second] + (i - in.second) * min(o[i], in.first) * d;
          }
      }
      
      int main() {
          int _;
          scanf("%d", &_);
          while(_ --) {
              scanf("%d", &n);
              for(int i = 1; i <= n; i ++) {
                  scanf("%d", &o[i]);
              }
              memset(dpl, 0, sizeof(dpl));
              memset(dpr, 0, sizeof(dpr));
              solve(dpl, 1, n + 1);
              solve(dpr, n, 1 - 1);
              int maxv = INF;
              for(int i = 1; i <= n - 1; i ++) {
                  maxv = min(dpl[i] + dpr[i + 1], maxv);
              }
              printf("%d\n", dpl[n] - maxv);
          }
          return 0;
      }
      
      /**************************************************************
          Problem: 1838
          User: 24862486
          Language: C++
          Result: AC
          Time:92 ms
          Memory:2520 kb
      /****************************************************************/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值