装满杯子需要的最短时长[贪心模拟 || 全局贪心]

前言

碰到局部选择,加上结果最值导向,基本就是不断最优解直到求到全局最值,经典的贪心思维,不断选择最优方向,就是一种贪心模拟;而有些问题,可以做到全局角度观察,挖掘其数学规律,以常数级复杂度完成贪心求解。

一、装满杯子需要的最短时长

二、贪心

1、贪心模拟

// 抽象,别看冷温热,就是三种不同的水,以及数量。
// 两种选择,要么装两杯不同类型的水,要么任意一杯。
// 只有两种选择,要在最短时间内装满所有水,必须贪心着装,即先一次装2杯。
// 关键问题:怕最后只剩一种类型的水n杯,根据规则,此时耗的不是(n+1)/2秒,而是n秒。
// 所以要先把最多水杯哪一类先装起,采用双装的方式,实在没没选择了,才能选择单装的方式。
// 注:最多水杯那一类随着剩余水杯数的变换,也在改变。

// case : 抽象 a >= b >= c,如果b != 0,则减少2杯水,否则减少一杯水,减至没有水杯。

func fillCups(amount []int) int {
    a,b,c := sortABC(amount[0],amount[1],amount[2])
    cnt := 0
    for ; a + b + c > 0;cnt++ {
        a--
        if b > 0 {
            b--
        }
        a,b,c = newABC(a,b,c)
    }
    return cnt
}
// 由于a >= b,要么ab一起移动到后面,要么b移动到后面。
func newABC(a,b,c int)(int,int,int){
    if a > c && b < c{
        b,c = c,b    
    }else if a <= c {
        a,b,c = c,a,b
    }
    return a,b,c
}
func sortABC(a,b,c int)(int,int,int){
    if a < b {
        a,b = b,a
    }
    if b < c {
        b,c = c,b
    }
    if a < b {
        a,b = b,a
    }
    return a,b,c
}

2、全局贪心

// 下面是数学法,从整体角度看,直接O(1)
// 最多一类水杯数 > 其他两类水杯数之和,就直接返回最大的即可。

// 反之,由于第2/3种水杯之和被限定了,a < b + c <= 2a,
// 先让b和c一起接,接到b+c == a时,或者b+c = a - 1时,再把b+c理解成一个整体,和a一起接。
// 所以按贪心,全是两杯水一起接,最多有一次单次接水,直接返回三者之和除2取上整。

func fillCups(amount []int) int {
    a,b,c := amount[0],amount[1],amount[2]
    if a >= b + c {
        return a
    }
    if b >= a + c {
        return b
    }
    if c >= a + b {
        return c
    }
    return (a + b + c + 1) >> 1
}

总结

1)贪心模拟,不断模拟选择最优方向,直至最后的全局最优解。
2)全局贪心,挖掘数据规律,从全局的角度贪心,一次性获得最优解,需要概括整个模拟贪心的过程。

参考文献

[1] LeetCode 装满杯子需要的最短时长

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值