1793 - 好子数组的最大分数 - 单调栈 - 贪心 - 枚举

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

题目描述

[1793] 好子数组的最大分数

  • https://leetcode-cn.com/problems/maximum-score-of-a-good-subarray/

给你一个整数数组 nums (下标从 0 开始)和一个整数 k 。

一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i+1], …, nums[j]) * (j - i + 1) 。一个 好 子数组的两个端点下标需要满足 i <= k <= j 。

请你返回 好 子数组的最大可能 分数 。

示例 1:

输入:nums = [1,4,3,7,4,5], k = 3
输出:15
解释:最优子数组的左右端点下标是 (1, 5) ,分数为 min(4,3,7,4,5) * (5-1+1) = 3 * 5 = 15 。
示例 2:

输入:nums = [5,5,4,5,4,1,1,1], k = 0
输出:20
解释:最优子数组的左右端点下标是 (0, 4) ,分数为 min(5,5,4,5,4) * (4-0+1) = 4 * 5 = 20 。

提示:

1 <= nums.length <= 10^5
1 <= nums[i] <= 2 * 10^4
0 <= k < nums.length

Related Topics
  • 贪心
  • 单调栈
  • 枚举

题目剖析&信息挖掘

先通过朴素思想设计出算法框架,然后针对算法中的核心过程进行优化。

解题思路

方法一 贪心+最小栈+枚举

解析

以例1为例。

nums = [1,4,3,7,4,5], k = 3

每次枚举i, 以nums[i]为最小值,查看i向左右延伸,延伸的下标记为x, y。
在这里插入图片描述

以i=2为例,用3向左,右扩展,只要比自己大或等于,就往外扩。最终X=1, Y=5。

整体枚举过程如下表。

在这里插入图片描述

如x<=k && k<=y 就把 nums[i]*(y-x+1)记为可行解。最终答案 为 max(nums[i]*(y-x+1))

黄色行为最佳答案。

复杂度分析,由于每次计算x, y都要O(n)复杂度,最终复杂度为O(n^2)。

优化

上述的关键点在于x,y的计算。

定义left[],right[] ,x=left[i], y=right[i]

如果可以找到一种方法,去快速计算出数组left[],right[]。那么问题应该可以解决。

由于left, right只是方向不同,计算逻辑应该是一样。所以先看left

可以先尝试几种情况

left[0]=0 这是肯定的,左边没有元素了。

当i>0时。如果nums[i]>nums[i-1], left[i]=i。

nums[i]==nums[i-1] , left[i]=left[i-1] 数字一样,向左延伸肯定也是一样的。

nums[i]<nums[i-1], 要向左寻找第一个小于自己的数字下标j, left[i]=left[j+1]

以上图为例计算left[4],

left[0]=0, left[1]=1, left[2]=1,left[3]=3已经计算好了。

最终j=0, left[4]=left[1]=1。

比较数字分别(从右到左)是[ 7,3,4,1].

可以发现4比3大,当比较nums[2]与nums[4]时,就已经知道nums[1]>nums[4]。完全是可以省略的。

这里可以引入单调栈数据结构。此栈如其名,就是栈里的数据就是单调递增的。

复杂度,由于每个元素最多入栈出栈一次,所以总体复杂度是O(n)

思路
func maximumScore(nums []int, k int) int {
	left := make([]int, len(nums))
	right := make([]int, len(nums))

	stack := []int{}

	for i, n := range nums {
		left[i]=-1
		for len(stack)>0 {
			lastInd := len(stack)-1
			if nums[stack[lastInd]] < n{
				break
			}
			left[i]=left[stack[lastInd]] // 更新答案,当遇到nums[stack[lastInd]] < n时 left[i] = left[stack[lastInd]]
			stack = stack[:lastInd]
		}
		if left[i]==-1 { // 说明栈中没有自己大的。
			left[i]=i
		}
		stack = append(stack, i)
	}
	
	
	for i:=len(nums)-1;i>=0;i-- {
		// 同上
	}
	best :=0
	for i:=0;i<len(nums);i++ {
		if left[i]<=k && right[i]>=k {
			best = max(best, (right[i]-left[i]+1) * nums[i])
		}
	}

	return best
}
注意
  • 当栈为空时,left[i], right[i]=i
知识点
  • 贪心
  • 单调栈
  • 枚举
复杂度
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
代码实现

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func maximumScore(nums []int, k int) int {
	left := make([]int, len(nums))
	right := make([]int, len(nums))

	stack := []int{}

	for i, n := range nums {
		left[i]=-1
		for len(stack)>0 {
			lastInd := len(stack)-1
			if nums[stack[lastInd]] < n{
				break
			}
			left[i]=left[stack[lastInd]]
			stack = stack[:lastInd]
		}
		if left[i]==-1 {
			left[i]=i
		}
		stack = append(stack, i)
	}

	stack = []int{}
	for i:=len(nums)-1;i>=0;i-- {
		right[i]=-1
		for len(stack)>0 {
			lastInd := len(stack)-1
			if nums[stack[lastInd]] < nums[i]{
				break
			}
			right[i]=right[stack[lastInd]]
			stack = stack[:lastInd]
		}
		if right[i]==-1 {
			right[i]=i
		}
		stack = append(stack, i)
	}
	best :=0
	for i:=0;i<len(nums);i++ {
		if left[i]<=k && right[i]>=k {
			best = max(best, (right[i]-left[i]+1) * nums[i])
		}
	}

	return best
}


本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言多机调度问题是指将n个作业分配给m台机器,使得完成所有作业的时间最短。这是一个经典的贪心算法问题。在这个问题中,我们需要将n个作业分配给m台机器,使得每台机器的工作时间尽可能平均,从而使得完成所有作业的时间最短。具体实现可以使用两种方法:机器数大于待分配作业数和机器数小于待分配作业数。前者可以直接将每个作业分配给一台机器,后者则需要使用贪心算法,将每个作业分配给当前工作时间最短的机器。 代码实现如下(C语言): ``` #include <stdio.h> #define N 8 //作业数 #define M 5 //机器数 int s[M] = {0,0,0};//每台机器当前已分配的作业总耗时 int main(void) { int time[N] = {17,15,11,9,8,5,4,2};//处理时间按从大到小排序 int maxtime = 0; if(M >= N) { maxtime = setwork1(time,N); } else { maxtime = setwork2(time,N); } printf("最多耗费时间%d。",maxtime); } //机器数大于待分配作业数 int setwork1(int t[],int n) { int i; for(i=0;i<n;i++) { s[i] = t[i]; } int ma = max(s,N); return ma; } //机器数小于待分配作业数 int setwork2(int t[],int n) { int i; int mi = 0; for(i=0;i<n;i++) { mi = min(M); printf("第%d号作业,时间和最小的机器号为%d.时间和为%d:\n",i,mi,s[mi]); s[mi] = s[mi]+t[i]; } int ma = max(s,M); return ma; } //求出目前处理作业的时间和最小的机器号 int min(int m) { int min = 0; int i; for(i=1;i<m;i++) { if(s[min] > s[i]) { min = i; } } return min; } //求最终结果(最长处理时间) int max(int s[],int num) { int max = s[0]; int i; for(i=1;i<num;i++) { if(max < s[i]) { max = s[i]; } } return max; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值