前言
碰到局部选择,加上结果最值导向,基本就是不断最优解直到求到全局最值,经典的贪心思维,不断选择最优方向,就是一种贪心模拟;而有些问题,可以做到全局角度观察,挖掘其数学规律,以常数级复杂度完成贪心求解。
一、装满杯子需要的最短时长
二、贪心
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)全局贪心,挖掘数据规律,从全局的角度贪心,一次性获得最优解,需要概括整个模拟贪心的过程。