3.11 动态规划/双指针/单调栈

1.加油站:

在一条环路上有 n个加油站,其中第 i个加油站有汽油 gas[i]升。
你有一辆油箱容量无限的的汽车,从第 1个加油站开往第 i+1个加油站需要消耗汽油 cost[i]升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回-1 。如果存在解,则 保证 它是 唯一 的。

gas = input().split()
cost = input().split()
sum1 = 0
for i in  range(len(gas)):
    gas[i] = int(gas[i])
    cost[i] = int(cost[i])
def jyz(gas,cost):
    for i in range(len(gas)):
        gas[i] = gas[i] - cost[i]
    gas = list(gas)
    for i in range(len(gas)):#遍历所有可能的起点
        sum = 0
        if gas[i] < 0:#起点为负数,直接跳过
            continue
        else:
            for j in range(i,len(gas)):#对可能的起点进行遍历
                sum += gas[j]
                if sum < 0:

                    break
            for j in range(0,i):#对可能的起点进行遍历
                sum += gas[j]
                if sum < 0:
                    i = i + 1 +j#如果i不可以作为起点,那么后面的数一定也不可以作为起点,所以直接跳过
                    break
            if sum >= 0:
                return i

    return -1
print(jyz(gas,cost))

上述代码可以优化的点在于,下一次的循环可以从i+j+1的地方开始,由于若从i无法走到j,则从i+1位置作为起点也无法走到,由于从i开始到i+1的地方时,i+1的初始油数一定非负,那意味着从i+1处启动为0,必然也不用参与循环,有效减缓程序时间。

2.接雨水

这道题以下给出三种解决方法:

1).双指针

通过左右指针指向两侧的遇到的最值,找到最大值较小的一侧后,由于水高最高和当前遇到最高的墙高(指针的值)一样,意味着用墙高减去每一个位置的墙数,可以得到每一个位置的可能的储水量。

​
 height = list(map(int, input().split()))
 left, right = 0, len(height) - 1
 left_max, right_max = 0, 0
 ans = 0

 while left < right:
     if height[left] < height[right]:
         # 左边较低,处理左边
         if height[left] >= left_max:
             left_max = height[left]
         else:
             ans += left_max - height[left]
         left += 1
     else:
         # 右边较低,处理右边
         if height[right] >= right_max:
             right_max = height[right]
         else:
             ans += right_max - height[right]
         right -= 1

 print(ans)

​

2).动态规划

本题对于动态规划的方法,需要先从左往右遍历一边数组,得到左侧的最大值组,右侧同理,之后对两个数组同时遍历,取得当前位置上两个数组值的最小值作为当前位置的最大水位,再减去位置上的墙数,最后累加则得到了储水量。


height = list(map(int, input().split()))
n = len(height)

# 构建左侧最大高度数组
left_max = [0] * n
left_max[0] = height[0]
for i in range(1, n):
    left_max[i] = max(left_max[i - 1], height[i])

# 构建右侧最大高度数组
right_max = [0] * n
right_max[n - 1] = height[n - 1]
for i in range(n - 2, -1, -1):
    right_max[i] = max(right_max[i + 1], height[i])

# 计算结果
ans = 0
for i in range(n):
    ans += min(left_max[i], right_max[i]) - height[i]

print(ans)

3).单调栈

单调栈(存元素下标)需要保持栈内元素单调,若下一个读入栈的元素不满足单调增,则对大于该数的所有元素逐个弹出,弹出元素时计算当前元素*(当前位置i - 栈顶元素下标 -1)首尾的话可以添加一个元素-1避免首位有减1的问题,或者可以单独对首尾添加判断。单调递减则相反,本题使用单调递减的栈,逐个计算两个高墙间的所有位置,并且减去每一个位置的墙数,累加得到结果。

height = list(map(int, input().split()))
stack = []
ans = 0

for current in range(len(height)):
    while stack and height[current] > height[stack[-1]]:
        top = stack.pop()
        if not stack:
            break
        distance = current - stack[-1] - 1
        bounded_height = min(height[current], height[stack[-1]]) - height[top]
        ans += distance * bounded_height

    stack.append(current)

print(ans)

3.单调栈例题:面试题: 求最大矩形面积,
恰定一个非负数组arr,每个坐标i横坐标x的位置。arr[i]表示当前位置宽为1的矩形高度,求arr中可以画出的最大矩形的面职。

这里的栈需要单调增,以找到最大的面积

def largestRectangleArea(heights):
    stack = []  # 初始化一个空栈
    max_area = 0  # 初始化最大面积

    for i, h in enumerate(heights):#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列
        # 开始位置的柱形高度已经不是递增的了
        # 确定右边界为 i,左边界为栈顶元素之后的那个元素
        while stack and heights[stack[-1]] > h:#如果栈不为空且栈顶元素大于当前元素,栈顶是左边界
            top = stack.pop()#弹出栈顶元素
            width = i if not stack else (i - stack[-1] - 1)#如果栈为空,宽度为i,否则为i-栈顶元素-1
            max_area = max(max_area, heights[top] * width)
        stack.append(i)

    # 遍历结束后清空栈,确定右边界为数组长度
    while stack:
        top = stack.pop()
        width = len(heights) if not stack else (len(heights) - stack[-1] - 1)
        max_area = max(max_area, heights[top] * width)

    return max_area


# 测试代码
heights = list(map(int, input().split(',')))  # 使用逗号', '分隔的输入
print(largestRectangleArea(heights))

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值