贪心算法part4 | ● 860.柠檬水找零 ● 406.根据身高重建队列 ● 452. 用最少数量的箭引爆气球

文章目录

  • 860.柠檬水找零
    • 思路
    • 思路代码
  • 406.根据身高重建队列
    • 思路
    • 思路代码
    • 官方题解
    • 代码
    • 困难
  • 452. 用最少数量的箭引爆气球
    • 思路
    • 思路代码
    • 官方题解
    • 代码
    • 困难
  • 今日收获


860.柠檬水找零

860.柠檬水找零

思路

所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。

思路代码

func lemonadeChange(bills []int) bool {
    cash:=[2]int{}
    for _,n:=range bills{
        if n==5{
            cash[0]++
        }else if n==10{
            cash[0]--
            cash[1]++
        }else{
            if cash[1]>0{
               cash[1]--
               cash[0]-- 
            }else{
                cash[0]-=3
            }            
        }
        if cash[0]<0{
            return false
        }
    }
    return true
}

406.根据身高重建队列

406.根据身高重建队列

思路

两个贪心
1.局部最优身高从高到低并且同身高符合要求
2.局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性
全局最优:最后都做完插入操作,整个队列满足题目队列属性

思路代码

func reconstructQueue(people [][]int) [][]int {
    sort.Slice(people, func(i, j int) bool {
        if people[i][0] == people[j][0] {
            return people[i][1] < people[j][1]
        }
        return people[i][0] > people[j][0]
    })
    res :=[][]int{}
    for _,n:=range people{
        if len(res)==0{
            res=append(res,n)
            continue
        }
        count:=n[1]
        for i2,n2:=range res{
            if n2[0]>=n[0]{
                count--
            }
            if count<0{
                res=append(res,[]int{})
                copy(res[i2+1:],res[i2:])
                res[i2]=n
                break
            }
            
            if count==0{
                if i2==len(res){
                    res=append(res,n)
                }else{
                    res=append(res,[]int{})
                    copy(res[i2+2:],res[i2+1:])
                    res[i2+1]=n
                }               
                break
            }
        }
    }
    return res
}

官方题解

优化插入

代码

func reconstructQueue(people [][]int) [][]int {
    // 先将身高从大到小排序,确定最大个子的相对位置
    sort.Slice(people, func(i, j int) bool {
        if people[i][0] == people[j][0] {
            return people[i][1] < people[j][1]   // 当身高相同时,将K按照从小到大排序
        }
        return people[i][0] > people[j][0]     // 身高按照由大到小的顺序来排
    })

    // 再按照K进行插入排序,优先插入K小的
	for i, p := range people {
		copy(people[p[1]+1 : i+1], people[p[1] : i+1])  // 空出一个位置
		people[p[1]] = p
	}
	return people
}

困难

本题有两个维度,h和k,看到这种题目一定要想如何确定一个维度,然后再按照另一个维度重新排列。

其实如果大家认真做了135. 分发糖果 (opens new window),就会发现和此题有点点的像。

在135. 分发糖果 (opens new window)我就强调过一次,遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。

如果两个维度一起考虑一定会顾此失彼。

对于本题相信大家困惑的点是先确定k还是先确定h呢,也就是究竟先按h排序呢,还是先按照k排序呢?

如果按照k来从小到大排序,排完之后,会发现k的排列并不符合条件,身高也不符合条件,两个维度哪一个都没确定下来。

那么按照身高h来排序呢,身高一定是从大到小排(身高相同的话则k小的站前面),让高个子在前面。

此时我们可以确定一个维度了,就是身高,前面的节点一定都比本节点高!

那么只需要按照k为下标重新插入队列就可以了。


452. 用最少数量的箭引爆气球

452.用最少数量的箭引爆气球

思路

注意先对气球排序
局部最优,只从重叠部分射

思路代码

func findMinArrowShots(points [][]int) int {
    cross:=[][]int{}
    sort.Slice(points, func (i,j int) bool {
        return points[i][0] < points[j][0]
    })
    for _,ballon:=range points{
        if len(cross)==0{
            cross=append(cross,ballon)
        }
        notcross:=true
        for _,xy:=range cross{
            if ballon[0]<=xy[1]&&ballon[1]>=xy[0]{
                xy[1]=min(ballon[1],xy[1])
                xy[0]=max(ballon[0],xy[0])
                notcross=false
            }
        }
        if notcross{
            cross=append(cross,ballon)
        }
    }
    return len(cross)
}

func min(i,j int) int{
    if i<j{
        return i
    }
    return j
}

func max(i,j int) int{
    if i>j{
        return i
    }
    return j
}

官方题解

直觉上来看,貌似只射重叠最多的气球,用的弓箭一定最少,那么有没有当前重叠了三个气球,我射两个,留下一个和后面的一起射这样弓箭用的更少的情况呢?

尝试一下举反例,发现没有这种情况。

那么就试一试贪心吧!局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。

代码

func findMinArrowShots(points [][]int) int {
    var res int = 1  //弓箭数
    //先按照第一位排序
    sort.Slice(points, func (i,j int) bool {
        return points[i][0] < points[j][0]
    })

    for i := 1; i < len(points); i++ {
        if points[i-1][1] < points[i][0] {  //如果前一位的右边界小于后一位的左边界,则一定不重合
            res++
        } else {
            points[i][1] = min(points[i - 1][1], points[i][1]); // 更新重叠气球最小右边界,覆盖该位置的值,留到下一步使用
        }
    }
    return res
}
func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}

困难

模拟射气球过程


今日收获

本题有两个维度,h和k,看到这种题目一定要想如何确定一个维度,然后再按照另一个维度重新排列。

其实如果大家认真做了135. 分发糖果 (opens new window),就会发现和此题有点点的像。

在135. 分发糖果 (opens new window)我就强调过一次,遇到两个维度权衡的时候,一定要先确定一个维度,再确定另一个维度。

如果两个维度一起考虑一定会顾此失彼。

对于本题相信大家困惑的点是先确定k还是先确定h呢,也就是究竟先按h排序呢,还是先按照k排序呢?

如果按照k来从小到大排序,排完之后,会发现k的排列并不符合条件,身高也不符合条件,两个维度哪一个都没确定下来。

那么按照身高h来排序呢,身高一定是从大到小排(身高相同的话则k小的站前面),让高个子在前面。

此时我们可以确定一个维度了,就是身高,前面的节点一定都比本节点高!

你可能感兴趣的:(算法题,贪心算法,算法,java)