问题
三个水杯A、B、C容量分别为11L、5L、6L,现A中有10L,B中有1L水,每次必须倒水方倒完或者被倒水方加满,最终A中装8L水。
思路
可以用递归方法解决此问题。
(1)每次倒水方式有6种可能,分别是A->B、A->C、B->A、B->C、C->A、C->B。
(2)记录每次倒完水A、B、C种的水量,每次记录都不相同。
(3)若有重复记录,则此次操作失败,尝试下一种倒水方式。
(4)若6种都失败,删除上次倒水记录,回归到上一次倒水的下一种倒水方式。
(5)依次类推,直到倒出A种8L水,层层返回成功。
实现(golang版)
package main
import (
"fmt"
)
var CUP1 = cup{10, 11}
var CUP2 = cup{1, 5}
var CUP3 = cup{0, 6}
type cup struct {
lv int // 水位
cap int // 容积
}
func (c *cup) level() int {
return c.lv
}
func (c *cup) capacity() int {
return c.cap
}
func (c *cup) validate() bool {
return c.lv <= c.cap
}
func (from *cup) pour(to *cup) bool {
if from.lv < 0 || to.lv < 0 {
panic("water level error")
}
// from有水,并且to没满
if from.lv != 0 && to.lv != to.cap { // 10, 5
// to可以倒满
if toLeft := to.cap - to.lv; toLeft < from.lv {
to.lv = to.cap
from.lv -= toLeft
} else { // to不能倒满
to.lv += from.lv
from.lv = 0
}
return true
}
return false
}
func (c *cup) String() string {
return fmt.Sprintf("lv: %2d, cap: %2d\n", c.lv, c.cap)
}
type PourWater struct {
cup1 cup
cup2 cup
cup3 cup
history [][3]cup
}
// 最终要求,一号杯装8升水
func (p *PourWater) last() bool {
return p.cup1.level() == 8
}
func (p *PourWater) pour() bool {
if !p.cup1.validate() || !p.cup2.validate() || !p.cup3.validate() {
panic("cup error: lv > cap")
}
// 判断该当前步骤是否重复
current := [3]cup{p.cup1, p.cup2, p.cup3}
for _, v := range p.history {
if v == current {
return false
}
}
// 将当前步骤放入历史操作队列中
p.history = append(p.history, current)
//fmt.Println(p)
if p.last() {
return true
}
// cup1 -> cup2
a, b, c := p.cup1, p.cup2, p.cup3
if p.cup1.pour(&p.cup2) && p.pour() {
return true
}
// cup1 -> cup3
p.cup1, p.cup2, p.cup3 = a, b, c
if p.cup1.pour(&p.cup3) && p.pour() {
return true
}
// cup2 -> cup1
p.cup1, p.cup2, p.cup3 = a, b, c
if p.cup2.pour(&p.cup1) && p.pour() {
return true
}
// cup2 -> cup3
p.cup1, p.cup2, p.cup3 = a, b, c
if p.cup2.pour(&p.cup3) && p.pour() {
return true
}
// cup3 -> cup1
p.cup1, p.cup2, p.cup3 = a, b, c
if p.cup3.pour(&p.cup1) && p.pour() {
return true
}
// cup3 -> cup2
p.cup1, p.cup2, p.cup3 = a, b, c
if p.cup3.pour(&p.cup2) && p.pour() {
return true
}
// 6种步骤都不成功,退回上一步操作(类似悔棋)
p.history = p.history[:len(p.history)-1]
//fmt.Println(p)
return false
}
func (p *PourWater) String() string {
var s string
for i := 0; i < len(p.history); i++ {
s += fmt.Sprintf("%d,%d,%d -> ", p.history[i][0].level(), p.history[i][1].level(), p.history[i][2].level())
}
return s
}
func main() {
pw := &PourWater{CUP1, CUP2, CUP3, make([][3]cup, 0)}
pw.pour()
fmt.Println(pw)
}
结果:10,1,0 -> 6,5,0 -> 6,0,5 -> 1,5,5 -> 1,4,6 -> 7,4,0 -> 7,0,4 -> 2,5,4 -> 2,3,6 -> 8,3,0 ->