特记
专为某人写的题解,记住哟,要思考思考
题意
给你 n 个墙壁,墙壁中间是一些房子村庄,然后就是老天开始下雨了,将墙壁之间的空隙给填满了,一句话:就是发洪灾啦,然后呢,政府需要泄洪,于是在这些墙壁之间打通一个下水管,将这里的水给排出去,最后问打通哪里的下水管能够排出去的水最多,输出最终结果
解题思路
思路一
如果当前我们选择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 /****************************************************************/