题解
难度:较难
知识点:分割字符串、数组、数学逻辑
解题思路:这道题主要在数学逻辑上具有较大的难度。解决这种数组问题,一定要想好数据的保存方式,再者这道题在输入中涉及到了字符串的分割,只有将字符串分割出来保存进数组才能使用这些数字。整道题将涉及到比较难的数学思维,以下将逐一介绍方法。
方法一:暴力解算(不能运行,运行超时,但是一种解决思维)
因为需要计算所有的盛水量,所以加入可以计算出每一个位置的盛水量,进行累加即可得到最终结果。但是怎么计算每一个位置的盛水量呢?这里只要确定每个位置,这需要寻找每一位置的左边界left和右边界right,然后比较[left, right]之间取较小值即可。
#include<iostream> #include <algorithm> #include<vector> using namespace std; int main() { vector<int> Heights; char c; int flag = 0; while((c = getchar()) != '\n'){ if(c != ','){ flag = flag * 10 + (c - '0'); } else { Heights.push_back(flag ); flag = 0; } } Heights.push_back(flag); long Sum = 0; int len=Heights.size(); for(int i=1;i<len-1;i++) { int LeftMax=0; int RightMax=0; for(int j = i - 1; j >= 0; j--) LeftMax= max(LeftMax,Heights[j]); for(int k=i+1;k<len;k++) RightMax= max(RightMax,Heights[k]); int minval = min(LeftMax, RightMax); if(minval > Heights[i]) Sum += minval - Heights[i]; } cout<<Sum<<endl; return 0; }
方法二(暴力法的优化)
前面因为在每个位置都需要进行两个for循环,在整个运行过程中导致超时,所以可以进行优化,直接采用两个数组保存每个位置最大左、右边界值即可。
#include<iostream> #include <algorithm> #include<vector> using namespace std; int main() { vector<int> Heights; char c; int flag = 0; while((c = getchar()) != '\n'){ if(c != ','){ flag = flag * 10 + (c - '0'); } else { Heights.push_back(flag ); flag = 0; } } Heights.push_back(flag ); long Sum = 0; int len=Heights.size(); int left_max[len]; int right_max[len]; left_max[0] = Heights[0]; right_max[len - 1] = Heights[len - 1]; for (int i = 1; i < len; i++) { left_max[i] = max(Heights[i], left_max[i - 1]); right_max[len - i - 1] = max(Heights[len-i-1], right_max[len-i]); } for (int i = 1; i < len - 1; i++) { Sum += min(left_max[i], right_max[i]) - Heights[i]; } cout<<Sum<<endl; return 0; }
方法三(双指针逼近)
使用两个指针标记,夹近这个值。
(1)设置两个索引值:
左边界索引left和右边界索引right
左边界索引:从数组头到数组尾方向,第一次出现下降趋势的位置。
右边界索引:从数组尾到数组头方看,第一次出现下降趋势的那个索引的位置。
(2)记录左边界和右边界的高度,记作leftHeight和rightHeight。如果left < right,说明左右边界还没有重合,令left加1。如果left位置能够存储雨水,则更新结果的值。如果此位置不能存储雨水,需要进入下一轮循环,更新leftHeight的值。如果left < right,说明左右边界还没有重合,令right减1。如果right位置能够存储雨水,则更新结果的值。如果right位置不能存储雨水,进入下一轮循环,更新rightHeight的值。
#include<iostream> #include <algorithm> #include<vector> using namespace std; int main() { //分割这个字符串,将数字保存进vector里 vector<int> Heights; char c; int flag = 0; while((c = getchar()) != '\n'){ if(c != ','){ flag = flag * 10 + (c - '0'); } else { Heights.push_back(flag); flag = 0; } } Heights.push_back(flag); long Sum = 0; int len=Heights.size(); int left = 0; while(left < len - 1 && Heights[left + 1] >= Heights[left]) { left++; } int right = len - 1; while(right >= 1 && Heights[right - 1] >= Heights[right]) { right--; } while(left < right) { int leftHeight = Heights[left]; int rightHeight = Heights[right]; if(leftHeight <= rightHeight) { while(left < right) { left++; if(Heights[left] < leftHeight) { Sum += leftHeight - Heights[left]; } else break; } } else { while(left < right) { right--; if(Heights[right] < rightHeight) { Sum += rightHeight - Heights[right]; } else break; } } } cout<<Sum<<endl; return 0; }