一、问题描述
小孩分油问题:两个小孩去打油,一人带了一个一斤的空瓶,另一个带了一个七两、一个三两的空瓶。原计划各打一斤油,可是由于所带的钱不够,只好两人合打了一斤油,在回家的路上,两人想平分这一斤油,可是又没有其它工具。试仅用三个瓶子(一斤、七两、三两)精确地分出两个半斤油来。
二、解题思路
选择合适的数据结构表示问题状态:
用向量(A, B, C)表示状态——其中A表示10斤油瓶的油量,B表示7斤油瓶中的油量,C表示3斤油瓶中的油量。
问题的初始状态:(10, 0, 0)
问题的目标状态:(5, 5, 0)
确定智能算子,即状态变化的规则:
规则号 | 规则 | 解释 |
---|---|---|
1 | (A, B, C) and B < 7 -> (3-C, 7, C) | 7两瓶不满时用10两瓶装满 |
2 | (A, B, C) and C < 3 -> (7-B, B, 3) | 3两瓶不满时用10两瓶装满 |
3 | (A, B, C) and B > 0 -> (10-C ,0, C) | 7两瓶不空时,倒空至10两瓶 |
4 | (A, B, C) and C > 0 -> (10-B, B, 0) | 3两瓶不空时,倒空至10两瓶 |
5 | (A, B, C) and B > 0 and B + C <= 3 -> (A, 0, B+C) | 7两瓶中的油全部倒入3两瓶中 |
6 | (A, B, C) and C > 0 and B + C <= 7 -> (A, B+C, 0) | 3两瓶中的油全部倒入7两瓶中 |
7 | (A, B, C) and B < 7 and B + C >= 7 -> (A, 7, B+C-7) | 用3两瓶中的油装满7两瓶 |
8 | (A, B, C) and C < 3 and B + C >= 3 -> (A, B+C-3, 3) | 用7两瓶中的油装满3两瓶 |
三、方法实现
数据结构与算法:
使用广度优先搜索对状态空间进行搜索
设置队列oilQueue用以保存待访问节点
设置列表visitedNodes用以保存已访问节点,减少重复搜索的时间
设置success_flag标签来指示搜索是否成功,初始为False。若循环结束success_flag仍为Flase则表示搜索失败,若为True则表示搜索成功。
具体步骤
1、 将初始状态(10, 0, 0)入队, 并加入visitedNodes列表中,表示该节点已访问
2、 若队列不空且success_flag为False,则队头元素出队。否则结束循环,根据success_flag判断是否成功搜索到了目标状态
3、 将该队头元素所有经智能算子变化得到的且未在vistedNodes列表中的元素入队,并加入visitedNodes列表中。且判断当前元素是否为目标状态,若是则将success_flag置为True,跳出循环,否则继续
4、 返回第2步
四、源代码
from queue import Queue
import time
class OilBottleState:
def __init__(self, A=10, B=0, C=0):
self.A = A
self.B = B
self.C = C
def __eq__(self, other):
return self.__dict__ == other.__dict__
class Node:
def __init__(self, state, findex):
self.state = state
# 用来存储父节点的在已访问节点列表中的下标,方便得到搜索路径
self.findex = findex
def __eq__(self