当青训营遇上码上掘金——我遇到了一个攒豆子的问题
问题描述
我的想法
这道题让我联想到在leetcode上刷到过的一道接雨水的题11. 盛最多水的容器 - 力扣(LeetCode) 不同的点在于那道题是在柱子中选出两根柱子使得盛水量最大,而这道题是直接求N根柱子的盛水量。
对这道题我主要有两个思路,从局部入手或是从整体入手。从局部入手
算法
以三根柱子为一个局部(中间的柱子可以是合并起来的整体),在一定条件下中间一根柱子采用与左或右柱子填满合并的方式变为新的整体,从头到尾不断重复填满的操作即可。
1.数组角标从左到右取三个,第一个存入stack栈中,称为left,第二个角标用mid保存,第三个角标用right保存,width=1; 2.比较a[left], a[mid], a[right]的值; 3.若a[right] > a[mid] > a[left], 则pop掉left,然后从第right个开始,重复1; 4.若a[left]>a[mid]>a[right],则依次将left,mid压入stack中,然后从第right个开始,重复1; 5.若全等,则重复3; 6.若a[left] > a[right] > a[mid], sum += (a[right] - a[mid]) * width;width++; mid = right; right++; 重复2;7.若a[right] > a[left] > a[mid], sum+=( a[right] - a[mid] ) * width; width++; mid = left; 弹出left;
8.此时,若stack为空,从第right个开始,重复1;9.若stack不为空,则用新的栈顶的left,重复2; 10.若right==n-1,计算后(也有可能不用计算)输出sum;
实现
实现过程不再赘述
从整体入手
算法
1.遍历整个数组,找到最长的一根柱子以及它的角标maxIdx(如果有相同长度的就选最后遍历到的那根);
2.从这根最长的柱子开始,分别向两边遍历,找左右两边第二长的柱子secIdx(左右两边各一根);3.以下以往左边遍历为例(右边类似),在maxIdx和secIdx之间,豆子(或者说是水)会填到第二长柱子的最高点,在这之间,每个柱子上方的水为第二长柱子的长度减去自生的长度,用leftSum来计算并存取:leftSum += (Height[secIdx] - Height[secIdx+1]) + ... + (Height[secIdx] - Height[maxIdx-1]),然后继续向左遍历;4.若找到的那根与maxIdx相邻,则把它赋为新的maxInx; 5.若secIdx=0,即为找到了数组的头,则结束查找; 6.向右边遍历类似,最后 Sum = leftSum + rightSum;实现(部分)
int getLeftMaxIndex(vector<int> a,int rightIndex)
{
int max = -1;
int LeftMaxIndex=-1;
for (int i = rightIndex-1; i >=0 ; i--)
{
if (a[i] >= max)
{
max = a[i];
LeftMaxIndex = i;
}
}
return LeftMaxIndex;
}
int findLeft(vector<int> a,int maxIndex)
{
int LeftMaxIndex=getLeftMaxIndex(a, maxIndex);
int Sum=0;
if (LeftMaxIndex == maxIndex - 1)
{
if (LeftMaxIndex == 0) return Sum;
else return findLeft(a, LeftMaxIndex);
}
else
{
for (int i = LeftMaxIndex + 1; i < maxIndex; i++)
{
Sum += a[LeftMaxIndex] - a[i];
}
if (LeftMaxIndex == 0)
{
return Sum;
}
else
{
int leftSum = Sum + findLeft(a, LeftMaxIndex);
return leftSum;
}
}
}
结语
最后谢谢大家的观看,如果我的算法有不妥之处欢迎指正,有更好的方法也可在评论区交流探讨一起进步。