Sai Charan is a professional programmer such that whatever he finds interesting, he would immediately convert it into a problem and try to get an algorithm for it which is the stepping stone for solving a problem. He saw water stagnation infront of his house after a heavy rain. So, he got an idea to write a program to find the amount of water stagnated, which is the inspiration for this problem
Problem Description
You are given an iron bar which is of dimension 1 unit. Iron bars are placed in a particular format for each test case, You must display the maximum units of accumulation of water between the iron bars if rainfall has occured in that building.
You can refer to the below image for clear understanding of the problem
Input
"The first line of the input contains an integer T denoting the number of test cases
"The first line of each test case contains a single integer N denoting the total number of bars that can be arranged horizontally
The second line of each test case contains N space-separated integers A0, A1, …, AN-1 denoting the bars which are placed vertically one above the other at each place A0,A1…AN-1 respectively
Output
For each test case, output a single line containing a single integer max which contains the maximum units of accumulation of water assuming a unit as the place between alternate bars
Constraints
1≤ T ≤ 100
1≤N≤ 1000
1≤ A0,A1,…AN-1 ≤ 1000
Example
Input:
2
6
3 0 0 2 0 4
6
3 0 3 3 3 3
Output:
10
3
思路分析:
首先,先明白什么情况下可以接到雨水。
对于第i个柱子来说,当左边的柱子和右边的柱子都比第i个柱子高时,第i个柱子就可以接到雨水。
更进一步分析,对于第i个柱子,向左扫描到最长的柱子l,向右扫描到最长的柱子r,那么该柱子可以接水的高度为min(h[l],h[r])-h[i].
因此就会有一个想法:对于每个柱子,找到左边的最大高度柱子和右边的最大高度柱子,然后计算接雨水的高度,接雨水的总体积就是每个柱子接到的雨水加起来。
这里可以想到用暴力扫描两边找最值,不过这样时间复杂度为O(n^2),一般都会超时。我们可以用dp打表,dp_left[i]表示第i个柱子左边最大值,dp_right[i]表示第i个柱子右边最大值,然后求出该柱子接水的高度。下面上dp代码。时间复杂度O(n),空间复杂度O(n).
方法一:dp
#include <iostream>
#define MAX 10010
using namespace std;
int h[MAX], dp_left[MAX], dp_right[MAX];
int main()
{
int T, N;
cin>>T;
while(T--)
{
cin>>N;
int ans = 0;
for(int i = 0;i < N;i++) scanf("%d",&h[i]);
dp_left[0] = h[0], dp_right[N-1] = h[N-1];
for(int i = 1;i < N;i++)
{
dp_left[i] = max(dp_left[i-1], h[i]);
dp_right[N-i-1] = max(dp_right[N-i],h[N-i-1]);
}
for(int i = 0;i < N;i++)
{
ans += (min(dp_left[i],dp_right[i])-h[i]);
}
cout<<ans<<endl;
}
return 0;
}
另一种方法是维护一个单调递减栈。为什么会想到这个方法呢?因为接雨水是高一低一高,先递减后递增。对于一个单调递减栈的栈顶元素可以直接访问左边第一个比它大的元素。我们维护一个单调递减栈,遇到比栈顶高的柱子就可以比较栈顶左右的柱子高度算出接水高度。这个需要画图模拟一下,然后找到规律。下面直接单调栈的代码。
方法二:单调栈
#include <iostream>
#include <stack>
#define MAX 10010
using namespace std;
int main()
{
int h[MAX];
int T, N;
cin>>T;
while(T--)
{
int ans = 0;
stack<int> st;
cin>>N;
for(int i = 0;i < N;i++)
{
cin>>h[i];
if(st.empty() || h[i] < h[st.top()]) //栈空或者高度比栈顶小,进栈
{
st.push(i);
}
else
{
while(!st.empty() && h[i] >= h[st.top()]) //h[i]比栈顶高,一直出栈直到h[i]比栈顶小或栈空,保持栈的严格递减
{
int tmph = h[st.top()];
st.pop();
if(!st.empty()) //若栈非空,累加接水体积
ans += (min(h[st.top()], h[i])-tmph)*(i-st.top()-1);
}
st.push(i);
}
}
cout<<ans<<endl;
}
return 0;
}