需求:現(xiàn)在只有兩只杯子,容量分別是:5升和7升,問題是:在只用這兩個(gè)杯子的前提下,如何才能得到4升水?假設(shè):水可以無限使用。
fun water(volume1 : Int, volume2: Int, volume: Int){
println("得到兩個(gè)杯子:${volume1}L,${volume2}L,目標(biāo)為${volume}L")
var x = 0
var cup1 = 0
var cup2 = 0
loop@ while (x != volume){
println("給 cup1 裝滿水")
println("cup1 向 cup2 倒水")
cup1 = volume1
while (cup2 + cup1 >= volume2){
cup1 -= (volume2 - cup2)
cup2 = 0
x = cup1
if(x == volume){
break@loop
}
println("cup2 水滿倒掉")
println("cup1 把剩下 $x 倒入 cup2")
}
if (cup2 + cup1 < volume2){
cup2 += cup1
cup1 = 0
x = cup1
}
println("cup1 水量 $cup1 cup2 水量 $cup2")
}
println("cup1 得到 ${x}L 水")
}
fun main(){
water(5, 7, 4)
}
輸出結(jié)果
得到兩個(gè)杯子:5L,7L,目標(biāo)為4L
給 cup1 裝滿水
cup1 向 cup2 倒水
cup1 水量 0 cup2 水量 5
給 cup1 裝滿水
cup1 向 cup2 倒水
cup2 水滿倒掉
cup1 把剩下 3 倒入 cup2
cup1 水量 0 cup2 水量 3
給 cup1 裝滿水
cup1 向 cup2 倒水
cup2 水滿倒掉
cup1 把剩下 1 倒入 cup2
cup1 水量 0 cup2 水量 1
給 cup1 裝滿水
cup1 向 cup2 倒水
cup1 水量 0 cup2 水量 6
給 cup1 裝滿水
cup1 向 cup2 倒水
cup1 得到 4L 水
如果將上面的杯子調(diào)換一下
fun main(){
water(7, 5, 4)
}
輸出結(jié)果
得到兩個(gè)杯子:7L,5L,目標(biāo)為4L
給 cup1 裝滿水
cup1 向 cup2 倒水
cup2 水滿倒掉
cup1 把剩下 2 倒入 cup2
cup1 水量 0 cup2 水量 2
給 cup1 裝滿水
cup1 向 cup2 倒水
cup1 得到 4L 水
算法看似行的通,但是還存在以下幾點(diǎn)缺陷:
- 無法判斷方案是否行的通,如果得不到對應(yīng)的水的話,會無限循環(huán)下去
- 這里只有cup1能得到對應(yīng)水,沒有判斷cup2得到相應(yīng)水的情況。例如,是water(2, 5, 4)的情況下,就會無限循環(huán),但是第二輪的時(shí)候cup2已經(jīng)得到了4L水。
針對以上問題,對算法做改進(jìn):記錄每次cup1和cup2的水量,如果出現(xiàn)重復(fù),則不會得到結(jié)果;對cup2的水量也進(jìn)行判斷。
改進(jìn)后的算法如下:
fun water(volume1 : Int, volume2: Int, volume: Int){
println("得到兩個(gè)杯子:${volume1}L,${volume2}L,目標(biāo)為${volume}L")
var record = mutableListOf<String>()
var cup1 = 0
var cup2 = 0
loop@ while (cup1 != volume && cup2 != volume){
if (record.contains("$cup1-$cup2")){
println("得不到結(jié)果")
break@loop
}else{
record.add("$cup1-$cup2")
}
println("給 cup1($cup1) 裝滿水")
cup1 = volume1
println("cup1($cup1) 向 cup2($cup2) 倒水")
while (cup2 + cup1 >= volume2){
cup1 -= (volume2 - cup2)
cup2 = 0
if(cup1 == volume){
break@loop
}
println("cup2($cup2) 水滿倒掉")
if (cup1 == 0){
continue@loop
}
println("把cup1($cup1) 剩下 $cup1 倒入 cup2($cup2)")
}
if (cup2 + cup1 < volume2){
cup2 += cup1
cup1 = 0
if (cup2 == volume){
break@loop
}
}
println("cup1 水量 $cup1 cup2 水量 $cup2")
}
println("cup1 得到 ${cup1}L 水")
println("cup2 得到 ${cup2}L 水")
println(record)
}
以上算法,去掉了多余的中間變量X,同時(shí)將while內(nèi)部的那個(gè)while用求余的方式表示,精簡了代碼
仔細(xì)考查,算法還存在一些不完美的地方。大致說幾個(gè)問題:
- 比如給出2個(gè)1L的杯子,得到2L的水。這個(gè)算法會輸入:不會得到結(jié)果。但是2個(gè)1L的杯子,不就是2L水嘛。
- 比如給出2個(gè)1L的杯子,得到1L的水。這個(gè)算法會先向cup1倒水,然后再倒入cup2,然后cup2倒掉,又回到0L和0L的兩個(gè)杯子,給出得不到結(jié)果。但是1L的cup1就直接能得到結(jié)果。
希望有更好的算法能與大家交流