农夫过河游戏的几种处理方法(DFS深度优先搜索,BFS广度优先搜索)

        农夫过河游戏规则:在左岸有农夫、狼、羊、菜,农夫需要想办法将狼、羊、菜最终都带到右岸,条件就是农夫不在的时候,狼会吃羊,羊会吃菜,而且每次只能带一样,或者不带。
这里会用到堆栈、队列、列表这样的数据结构,以及广度优先搜索和深度优先搜索两种算法。
代码来自 aaybvh 一个努力学习的在校生,由于代码有点长,需要一些解释,于是抽个时间,将代码做了很多详细的注释,希望可以帮助到他,有兴趣的可以看下:

DFS与BFS

class Queue(list):  # 队列的性质是先进先出,操作:队尾插入,队头删除
    def __init__(self):
        list.__init__(self)

    def Enqueue(self, s):  # 入队
        self.append(s)

    def Dequeue(self):  # 出队
        return self.pop(0)

    def Print(self):  # 打印队列
        print('count:%d' % (len(self)))
        for item in self:
            item.Print()

    def Empty(self):  # 是否为空队列
        if len(self) == 0:
            return True
        return False


class Stack(list):  # 堆栈的性质是先进后出,操作:栈顶进行插入和删除
    def __init__(self):
        list.__init__(self)

    def Push(self, s):  # 入栈
        self.append(s)

    def Pop(self):  # 出栈
        return self.pop()

    def Print(self):  # 打印堆栈,索引从大开始
        idx = len(self) - 1
        while idx >= 0:
            self[idx].Print()
            idx = idx - 1

    def PrintReverse(self):  # 翻转打印
        print('count:%d' % (len(self)))
        for item in self:
            item.Print()

    def Top(self):  # 栈顶元素
        return self[len(self) - 1]

    def Empty(self):  # 是否为空栈
        if len(self) == 0:
            return True
        return False


class State:
    '''
    农夫、狼、羊、菜的状态
        初始状态为(0,0,0,0)都在左岸,当全部运送到对岸(右岸)时状态为(1,1,1,1)
    '''

    def __init__(self, f, w, s, v):
        self.f = f
        self.w = w
        self.s = s
        self.v = v
        # 用于广度优先搜索
        self.father_list = MyList()
        self.direction = ''

    def SetDirection(self, d):  # 用于广度优先搜索
        self.direction = d

    def SetFather(self, f):
        self.father = f

    def __eq__(self, s):  # 重写__eq__方法,这样就可以直接用"=="来判断对象否相等(当对象属性都相等时)
        return self.w == s.w and self.f == s.f and self.s == s.s and self.v == s.v

    def Print(self):
        print('过河状态:(%d,%d,%d,%d)' % (self.f, self.w, self.s, self.v))

    def Copy(self, s):
        self.f = s.f
        self.w = s.w
        self.s = s.s
        self.v = s.v

    def IsValid(self):  # 状态是否可行
        if self.f == 0:  # 农夫在左岸
            if self.w == self.s and self.w == 1:  # 如果狼和羊在同岸且都在右岸,就不行
                return False
            elif self.s == self.v and self.s == 1:  # 如果羊和菜在同岸且都在右岸,也不行
                return False
            else:
                return True
        elif self.f == 1:  # 农夫在右岸
            if self.w == self.s and self.w == 0:  # 同理,狼和羊不能同时在左岸
                return False
            elif self.s == self.v and self.s == 0:  # 同理,羊和菜不能同时在左岸
                return False
            else:
                return True


class MyList(list):
    '''
    保存的是状态的列表
        状态是指农夫、狼、羊、菜在左岸还是右岸
    Has:是否存在这个状态
    Remove:删除指定状态
    '''

    def __init__(self):
        list.__init__(self)

    def Has(self, s):
        for item in self:
            if item == s:
                return True
        return False

    def Remove(self, s):
        for i in range(0, len(self)):
            if self[i] == s:
                return self.pop(i)


# 第i样东西送到右岸
def L(news, s, i):
    news.Copy(s)
    if news.f == 0:  # 农夫需在左岸
        if 0 == i:  # 农夫过河不带东西
            pass
        elif 1 == i and news.w == 0:  # 带狼过去,且在左岸
            news.w = 1
        elif 2 == i and news.s == 0:  # 带羊过去
            news.s = 1
        elif 3 == i and news.v == 0:  # 带菜过去
            news.v = 1
        else:
            return False
        news.f = 1  # 农夫到了右岸
        return True
    else:
        return False


# 第i样东西送到左岸
def R(news, s, i):
    news.Copy(s)
    if news.f == 1:  # 农夫需在右岸
        if 0 == i:  # 农夫过河不带东西
            pass
        elif 1 == i and news.w == 1:  # 将狼从右岸带到左岸
            news.w = 0
        elif 2 == i and news.s == 1:  # 带羊过去
            news.s = 0
        elif 3 == i and news.v == 1:  # 带菜过去
            news.v = 0
        else:
            return False
        news.f = 0  # 农夫到了左岸
        return True
    else:
        return False


def IsValid(s):
    if s.f == 0:  # 农夫在左岸
        if s.w == s.s and s.w == 1:  # 狼和羊不能同时在右岸
            return False
        elif s.s == s.v and s.s == 1:  # 羊和菜不能同时在右岸
            return False
        else:
            return True
    elif s.f == 1:  # 农夫在右岸
        if s.w == s.s and s.w == 0:  # 狼和羊不能同时在左岸
            return False
        elif s.s == s.v and s.s == 0:  # 羊和菜不能同时在左岸
            return False
        else:
            return True


def DFS(s, e, stack, close_list, direction):
    ''''
    深度优先搜索
        s:起始状态,e:结束状态。由于重写了__eq__方法,所以两个对象是可以直接做==比较
    '''
    if s == e:  # 起始状态更新到与结束状态一样就打印出来,意思就是东西已全部运送到了对岸
        global road_num
        road_num += 1
        print('第%d条路径:' % road_num)
        stack.Push(s)
        stack.PrintReverse()
        stack.Pop()
        return

    # 加入close_list列表,表示已经访问过
    close_list.append(s)
    stack.Push(s)
    if direction == 'L':  # 农夫在左岸
        for i in range(0, 4):
            news = State(0, 0, 0, 0)
            # 将东西带到右岸,而且要保证是有效状态以及没有被访问过,然后递归调用DFS,方向变为右岸
            if L(news, s, i) and news.IsValid() and close_list.Has(news) == False:
                DFS(news, e, stack, close_list, 'R')
    elif direction == 'R':  # 右岸带东西去左岸,同理
        for i in range(0, 4):
            news = State(0, 0, 0, 0)
            if R(news, s, i) and news.IsValid() and close_list.Has(news) == False:
                DFS(news, e, stack, close_list, 'L')
    close_list.Remove(s)  # 清空,为下一次的搜索做准备
    stack.Pop()


start_state = State(0, 0, 0, 0)
end_state = State(1, 1, 1, 1)
direction = 'L'
road_num = 0

# close_list表存放访问过的状态
close_list = MyList()
stack = Stack()

print('----------深度优先搜索-------------')
DFS(start_state, end_state, stack, close_list, direction)


def PrintPath(s, e, close_list):  # 广度优先搜索打印
    if s == e:
        global road_num
        road_num += 1
        print('第%d条路径:' % road_num)
        print('(%d,%d,%d,%d)' % (e.f, e.w, e.s, e.v))
        for state in close_list:
            print('(%d,%d,%d,%d)' % (state.f, state.w, state.s, state.v))
    # 加入到访问表中
    else:
        close_list.insert(0, s)
        for state in s.father_list:
            PrintPath(state, e, close_list)

    close_list.Remove(s)


def BFS(start_state, end_state):
    '''
    广度优先搜索
        start_state:起始状态和end_state:结束状态
    '''
    close_list = MyList()

    open_list = Queue()
    open_list.Enqueue(start_state)

    return_state = None
    while open_list.Empty() == False:
        state = open_list.Dequeue()
        # 判断状态state是否在已访问的列表当中
        if close_list.Has(state):
            state_real = close_list.Remove(state)
            state_real.father_list += state.father_list
            close_list.append(state_real)
            continue
        else:
            close_list.append(state)

        # 是否是目标状态
        if state == end_state:
            return_state = state
            continue

        if state.direction == 'L':  # 农夫在左岸
            for i in range(0, 4):
                news = State(0, 0, 0, 0)
                # 将东西带到右岸,而且要保证是有效状态以及没有被访问过,就添加到列表中,然后修改方向
                if L(news, state, i) and news.IsValid() and close_list.Has(news) == False:
                    news.father_list.append(state)
                    news.SetDirection('R')
                    open_list.Enqueue(news)
        else:
            for i in range(0, 4):
                news = State(0, 0, 0, 0)
                if R(news, state, i) and news.IsValid() and close_list.Has(news) == False:
                    news.father_list.append(state)
                    news.SetDirection('L')
                    open_list.Enqueue(news)
    return return_state


start_state.SetDirection('L')
end_state = BFS(start_state, end_state)
close_list = MyList()
print('----------广度优先搜索-------------')
road_num = 0
PrintPath(end_state, start_state, close_list)
'''
----------深度优先搜索-------------
第1条路径:
count:8
过河状态:(0,0,0,0)
过河状态:(1,0,1,0)
过河状态:(0,0,1,0)
过河状态:(1,1,1,0)
过河状态:(0,1,0,0)
过河状态:(1,1,0,1)
过河状态:(0,1,0,1)
过河状态:(1,1,1,1)
第2条路径:
count:8
过河状态:(0,0,0,0)
过河状态:(1,0,1,0)
过河状态:(0,0,1,0)
过河状态:(1,0,1,1)
过河状态:(0,0,0,1)
过河状态:(1,1,0,1)
过河状态:(0,1,0,1)
过河状态:(1,1,1,1)
----------广度优先搜索-------------
第1条路径:
(0,0,0,0)
(1,0,1,0)
(0,0,1,0)
(1,1,1,0)
(0,1,0,0)
(1,1,0,1)
(0,1,0,1)
(1,1,1,1)
第2条路径:
(0,0,0,0)
(1,0,1,0)
(0,0,1,0)
(1,0,1,1)
(0,0,0,1)
(1,1,0,1)
(0,1,0,1)
(1,1,1,1)
'''

从中我们也了解到,要将东西从左岸搬到右岸,从右岸搬到左岸,这里的岸就可以设置成两种状态,0为左岸,1为右岸每种状态都是农夫、狼、羊、菜四个元素,每个元素两种状态,然后通过它们一起呈现的状态来决定是否是可行状态,比如(0,1,1,0)农夫和菜在左岸,狼和羊在右岸,这样就不行,因为农夫不在身边,狼会吃掉羊。
我们通过农夫的来回过河,带走或不带走某个东西从而改变其状态,最终是(1,1,1,1)这样的状态,就表示农夫将狼、羊、菜都运送到了右岸。

其中的两种优先搜索方法,解释如下:
深度优先搜索算法(Depth First Search):简称DFS,是一种用于遍历或搜索树或图的算法。该算法沿着树的深度遍历树的节点,会尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。
深度优先搜索使用的是回溯思想,这种思想很适合使用「递归」来实现。而递归对问题的处理顺序,遵循了「后进先出」的规律。所以递归问题的处理,需要借助「堆栈」来实现。

广度优先搜索算法(Breadth First Search),又称宽度优先搜索,简称BFS,与深度优先搜索不同的是,广度优先搜索会先将与起始点距离较近的点搜索完毕,再搜索更远的点
BFS从起点开始,优先搜索离起点最近的点,然后由这个点最近的点扩展到其他稍近的点,这样一层一层扩展,就像水波扩散一样。

__eq__重写

其中对于__eq__重写的用法,再写个例子让大家更加清晰的了解,重写这个方法之后,就可以直接用“==”来判断两个对象是不是相等了。就是当对象的属性都相等时,这个对象是相等的。

#__eq__重写
class A(object):
    def __eq__(self, s):
        print("A __eq__ called")
        return self.v1 == s and self.v2==s
class B(object):
    def __eq__(self, s):
        print("B __eq__ called")
        return self.v1 == s and self.v2==s
a=A()
a.v1="hello"
a.v2="tony"
b=B()
b.v1='hello'
b.v2="chyichin"
print(a==b)
print(a.v1==b.v1)
print(a.v2==b.v2)

'''
A __eq__ called
B __eq__ called
False
True
False
'''

随机交换

如果单纯的只是让农夫将狼、羊、菜带到对岸的话,还可以一种比较讨巧的方法,就是让农夫随机的交换,因为农夫不在的时候,只需要狼和羊不在一起,或者羊和菜不在一起就可以,当然这样也会产生一些无效的步骤,然后我们将它们删除掉,这样也可以得到农夫过河的步骤。

import random

left = ['白菜', '羊', '狼']  # 左岸的东西带到右岸
right = ['空'] * 3  # 右岸为空
things = []     # 储存每一步所带的东西
cnt = 0     # 奇数:农夫在左岸,偶数农夫在右岸

while True:
    # 首先农夫在左岸(奇数)
    # 或者农夫在右岸,同样右岸可以是狼羊或羊白菜
    if cnt % 2 == 1 and ('狼' in left and '羊' in left or '羊' in left and '白菜' in left) \
            or cnt % 2 == 0 and ('狼' in right and '羊' in right or '羊' in right and '白菜' in right):
        left = ['白菜', '羊', '狼']
        right = ['空'] * 3
        things = []
        cnt = 0
        continue

    i = random.randint(-1, 2)  # -1,0,1,2表示带什么东西过河的索引,-1就是不带东西
    things.append(i)
    # 需带东西过河时,如果农夫在右岸,左岸有东西就交换,属于随机交换
    if i != -1 and cnt % 2 == 0 and left[i] != '空':
        left[i], right[i] = right[i], left[i]
    # 如果农夫在左岸,右岸有东西,进行交换
    elif i != -1 and cnt % 2 == 1 and right[i] != '空':
        left[i], right[i] = right[i], left[i]
    else:
        # 不带东西
        things[-1] = -1
    # 如果全部带到右岸就退出
    if right == ['白菜', '羊', '狼']:
        break
    cnt += 1

lit = ['不带', '白菜', '羊', '狼']
#-------无效的步骤设置为空数组,并移除掉-----
for i in range(len(things)-1):
    if things[i] == things[i+1]:
        things[i] = things[i+1] = []
while [] in things:
    things.remove([])
#-----------------------------------------
for i in things:
    print(lit[i+1], end='\t')

#羊      不带    白菜    羊      狼      不带    羊

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,农夫过河问题是一个经典的逻辑问题,题目描述如下: 一个农夫要带着一只狼、一只羊和一颗白菜过河,但是小船只能装下农夫和另外一件物品。如果农夫不在场,狼就会吃羊,羊不在场,白菜就会被吃。请问农夫如何才能安全地将这三样东西全部运到河的对岸? 以下是一个简单的 Python 实现: ```python class State: def __init__(self, farmer, wolf, goat, cabbage, boat): self.farmer = farmer # 0:在左岸,1:在右岸 self.wolf = wolf self.goat = goat self.cabbage = cabbage self.boat = boat # 0:在左岸,1:在右岸 def is_valid(self): if self.farmer not in [0, 1] or self.wolf not in [0, 1] or self.goat not in [0, 1] or self.cabbage not in [0, 1] or self.boat not in [0, 1]: return False if self.wolf == self.goat and self.farmer != self.wolf: return False if self.goat == self.cabbage and self.farmer != self.goat: return False return True def is_goal(self): return self.farmer == 1 and self.wolf == 1 and self.goat == 1 and self.cabbage == 1 def __eq__(self, other): return self.farmer == other.farmer and self.wolf == other.wolf and self.goat == other.goat and self.cabbage == other.cabbage and self.boat == other.boat def __hash__(self): return hash((self.farmer, self.wolf, self.goat, self.cabbage, self.boat)) class Node: def __init__(self, state, parent=None, action=None): self.state = state self.parent = parent self.action = action def expand(self): children = [] for a in ["farmer", "wolf", "goat", "cabbage"]: new_state = State(self.state.farmer, self.state.wolf, self.state.goat, self.state.cabbage, self.state.boat) setattr(new_state, a, 1 - getattr(self.state, a)) # 改变属性值 new_state.boat = 1 - self.state.boat # 船也过河了 child = Node(new_state, self, a) if new_state.is_valid(): children.append(child) return children def bfs(initial_state): initial_node = Node(initial_state) if initial_state.is_goal(): return initial_node frontier = [initial_node] explored = set() while frontier: node = frontier.pop(0) explored.add(node.state) for child in node.expand(): if child.state.is_goal(): return child if child.state not in explored and child not in frontier: frontier.append(child) return None initial_state = State(0, 0, 0, 0, 0) solution = bfs(initial_state) if not solution: print("No solution found!") else: path = [] node = solution while node: path.append(node) node = node.parent path.reverse() for node in path: print(node.state.farmer, node.state.wolf, node.state.goat, node.state.cabbage, node.state.boat, node.action) ``` 这里使用了 BFS 算法来搜索解决方案,首先定义了 `State` 类来表示状态,包含农夫、狼、羊、白菜和船的位置,以及一些辅助方法。然后定义了 `Node` 类来表示搜索树中的节点,包含一个状态和一个指向父节点的指针。最后定义了 `bfs` 函数来执行 BFS 算法搜索解决方案,返回最终状态的节点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寅恪光潜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值