每日一题 力扣2865 美丽塔Ⅰ
2865. 美丽塔 I
题目描述:
给你一个长度为 n
下标从 0 开始的整数数组 maxHeights
。
你的任务是在坐标轴上建 n
座塔。第 i
座塔的下标为 i
,高度为 heights[i]
。
如果以下条件满足,我们称这些塔是 美丽 的:
1 <= heights[i] <= maxHeights[i]
heights
是一个 山脉 数组。
如果存在下标 i
满足以下条件,那么我们称数组 heights
是一个 山脉 数组:
- 对于所有
0 < j <= i
,都有heights[j - 1] <= heights[j]
- 对于所有
i <= k < n - 1
,都有heights[k + 1] <= heights[k]
请你返回满足 美丽塔 要求的方案中,高度和的最大值 。
示例 1:
输入:maxHeights = [5,3,4,1,1] 输出:13 解释:和最大的美丽塔方案为 heights = [5,3,3,1,1] ,这是一个美丽塔方案,因为: - 1 <= heights[i] <= maxHeights[i] - heights 是个山脉数组,峰值在 i = 0 处。 13 是所有美丽塔方案中的最大高度和。示例 2:
输入:maxHeights = [6,5,3,9,2,7] 输出:22 解释: 和最大的美丽塔方案为 heights = [3,3,3,9,2,2] ,这是一个美丽塔方案,因为: - 1 <= heights[i] <= maxHeights[i] - heights 是个山脉数组,峰值在 i = 3 处。 22 是所有美丽塔方案中的最大高度和。示例 3:
输入:maxHeights = [3,2,5,5,2,3] 输出:18 解释:和最大的美丽塔方案为 heights = [2,2,5,5,2,2] ,这是一个美丽塔方案,因为: - 1 <= heights[i] <= maxHeights[i] - heights 是个山脉数组,最大值在 i = 2 处。 注意,在这个方案中,i = 3 也是一个峰值。 18 是所有美丽塔方案中的最大高度和。提示:
1 <= n == maxHeights <= 10^3
1 <= maxHeights[i] <= 10^9
思路:
尝试:贪心,先要找出可以放的最高的位置(可能不止一个);
然后以当前位置为界,向两边遍历,每次遍历需要更新两个内容:
1.如果当前高度和大于目前最大高度和,更换之
2.如果当前遍历位置小于本循环内目前最大限高,把限高换成小的。
代码:
有点问题,待改进
class Solution:
def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
max_h = max(maxHeights)
hill=maxHeights.index(max_h)#这是山峰位置
i,j=hill-1,hill+1
max_left,max_left_now=0,0
max_right,max_right_now=0,0#右侧最大和,以及目前高度
height_left,height_right=hill,hill#限高
while(i>=0):#往左遍历
max_left+=min(height_left,maxHeights[i])#左侧高度和
height_left=min(height_left,maxHeights[i])
i-=1
while j < len(maxHeights) :
max_right+=min(height_right,maxHeights[j])#右侧高度和
height_right=min(height_right,maxHeights[j])
j+=1
return max_h+max_left+max_right
问题在于,当峰值不唯一时存在问题,因此还是要按照遍历的方式,双重循环去做;或者想办法,找出所有的峰值点,然后依次进行遍历,对每次遍历得到的最大高度进行比较。
换一个:
class Solution:
def maximumSumOfHeights(self, maxHeights: List[int]) -> int:
n = len(maxHeights)
res = 0
for i in range(n):
pre, psum = maxHeights[i], maxHeights[i]
for j in range(i - 1, -1, -1):
pre = min(pre, maxHeights[j])
psum += pre
suf = maxHeights[i]
for j in range(i + 1, n):
suf = min(suf, maxHeights[j])
psum += suf
res = max(res, psum)
return res
其他思路:单调栈的思路
暴力法的思路是从山峰向两侧去构造一个高度之和最大的山脉数组,那么我们能否实现一个从两侧向山峰去构造这个高度之和最大的山脉数组呢?
不妨先考虑构造山峰的左侧,我们自左向右遍历 maxHeights[],对于 nums[i]:
若maxHights[i]>=nums[i-1],则 nums[i] 最高可以取到 maxHeights[i],为了使得高度之和最大,我们不妨贪心的就先令 nums[i]=maxHights[i]
否则,说明 nums[i-1] 太高了,为了使得高度之和最大,我们不妨贪心的令 nums[i-1] = maxHights[i]。同样的,当 nums[i-1] 的高度发生变化后,我们还要继续去判断 nums[i-2] nums[i-3] , ... 的高度是否需要变化
从上述分析中不难看出,在我们自左向右构造山峰的左侧时,需要经常出现 「回退」,因此这里我们可以考虑用一个栈来辅助实现这一过程:初始时向栈中添加 0 作为边界条件
如果 maxHeights[i]>= 栈顶元素,则说明我们可以直接令 nums[i] 为 maxHights[i]
否则我们需要回去修改之前的高度,不断弹出栈中的元素,直至 maxHights[i]>= 栈顶元素
当遍历到 i 后,我们便构造出了山脉的左侧显然根据上述的思路,如果我们在 i 的基础上继续遍历到 i+1,就可以构造出以 i+1 为山峰的山脉数组的左侧,因此这启发我们用两个数组 pre,suf 来分别记录以 i 为山峰时的左右两侧的高度之和(pre[i]=sum{nums[0:i]},suf[i]=sum{nums[i:n-1]})。这样之后只要再遍历一次,取最大的 pre[i]+suf[i]-maxHights[i] 即可。
class Solution {
typedef pair<size_t,int> PSI;
public:
long long maximumSumOfHeights(vector<int>& maxHeights) {
const size_t n = maxHeights.size();
stack<PSI> st;
st.emplace(-1, 0); // 哨兵
vector<long long> mark(n);
long long sum = 0;
for( size_t i = 0; i < n; ++i ) {
while( st.top().second > maxHeights[i] ) {
auto [j,h] = st.top();
st.pop();
sum -= (h-maxHeights[i])*(j-st.top().first);
}
st.emplace(i, maxHeights[i]);
mark[i] = sum;
sum += maxHeights[i];
}
st = stack<PSI>();
st.emplace(n, 0); // 哨兵
sum = 0;
long long ans = 0;
for( int i = n-1; i >= 0; --i ) {
while( st.top().second > maxHeights[i] ) {
auto [j,h] = st.top();
st.pop();
sum -= (h-maxHeights[i])*(st.top().first-j);
}
st.emplace(i, maxHeights[i]);
sum += maxHeights[i];
ans = max(ans, sum+mark[i]);
}
return ans;
}
};