递归问题解决倒水问题

问题

三个水杯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 ->

你可能感兴趣的:(递归问题解决倒水问题)