过河问题(图、BFS)

描述:

原题:3只羊和3只狮子过河,有1艘船只能容纳2只动物,当河岸上狮子数大于羊数,羊就会被吃掉,找到运输方法,让所有动物都过河。

类似推广:野人传教士过河;羊狼过河;有些问题描述时会加一个农夫(渡船人),但农夫往往不是影响因素。


思路:

看到类似问题,想到状态搜索,搜索方式一般有两种:DFSBFS,其都是对所有状态的一种搜索,直到搜索到目标状态


定义状态
1)题目意思是,原来岸边有3狮3羊,最后要安全渡河,变成0狮0羊,那么状态必有狮子数和羊数;
2)什么导致状态发生变化,是用船运送,那船在此岸还是彼岸?一开始船肯定在此岸,最后运输完成后,船必然是在彼岸,故船在哪个岸应该也要作为状态;
于是,定义状态state:

  • state = (m, n, k)
  • m: 羊数,0 <= m <= 3
  • n: 狮子数,0 <= n <= 3
  • k: 船在哪个岸,k = {0, 1}, 1表示在A岸(也就是此岸);0表示在B岸(也就是在彼岸)

那么,最终就是搜索A岸从(3,3,1)—> (0,0,0)
:理论上可能的状态一共有32种,即 4 * 4 * 2 = 32


定义决策(让状态发生变化的行为):
根据题意,船运输的羊和狮子的数量满足:
1)总数不超过2:m + n <= 2
2)羊数不小于狮子数:m >= n
那么,策略一共有以下5种:

(1,0), (0,1), (1,1), (2,0), (0,2)

例如,(1,0)表示运输羊1只狮子0只


BFS
就是从当前状态开始,遍历所有可能的决策,进行状态跳转,如下图:

1)搜索在向广度拓展,所以是典型的BFS搜索(如果是DFS,就是向深度拓展);
2)可以根据记忆(空间换时间),将已经发现过的状态剪枝,不重复搜索;
3)判断状态是否合法:

  • A岸 和 B岸的狮子数不大于羊数;
  • A岸 或 B岸的狮子数或羊数不能超过边界;

4)根据船在A岸还是B岸,修改状态跳转逻辑:

  • 船在A岸,则A岸狮子、羊的数量减少,B岸狮子、羊的数量增加;
  • 船在B岸,则A岸狮子、羊的数量增加,B岸狮子、羊的数量减少;

show the code

以下是python编写的算法,基于pythonds
详细可看注释,个人认为还是比较清晰的

from pythonds.graphs import Graph
from pythonds.basic import Queue
def solution():'''3只羚羊和3只狮子过河问题:1艘船只能容纳2只动物当河岸上狮子数大于羚羊数,羚羊就会被吃掉找到运输方法,让所有动物都过河'''# 定义合法的运输操作(i, j) , 例如(1, 0)表示运送羚羊1只,狮子0只opt = [(1, 0), (0, 1), (1, 1), (2, 0), (0, 2)]# 定义状态state(m, n, k), m表示羚羊数,n表示狮子数,k表示船在此岸还是彼岸# stateA 表示A岸(此岸)的状态;stateB 表示B岸(彼岸)的状态;# 初始状态stateA = (3, 3, 1)stateB = (0, 0, 0)# BFS搜索mygraph = Graph()myqueue = Queue()myqueue.enqueue((stateA, stateB))sequence = []  # 剪枝记录(最后发现,有效状态只有15种)sequence.append((stateA))while True:stateA, stateB = myqueue.dequeue()if stateA == (0, 0, 0):breakfor o in opt:# 一次从某岸到另一岸的运输if stateA[2] == 1:stateA_ = (stateA[0] - o[0], stateA[1] - o[1], stateA[2] - 1)stateB_ = (stateB[0] + o[0], stateB[1] + o[1], stateB[2] + 1)else:stateB_ = (stateB[0] - o[0], stateB[1] - o[1], stateB[2] - 1)stateA_ = (stateA[0] + o[0], stateA[1] + o[1], stateA[2] + 1)# 运输后if stateA_[0] and stateA_[0] < stateA_[1]:  # 此岸在有羊的情况下,如果狼大于羊,则吃掉continueelif stateB_[0] and stateB_[0] < stateB_[1]:  # 彼岸在有羊的情况下,如果狼大于羊,则吃掉continueelif stateA_[0] < 0 or stateA_[0] > 3 or stateA_[1] < 0 or stateA_[1] > 3:  # 边界continueelse:# 剪枝if stateA_ in sequence:continueelse:sequence.append(stateA_)myqueue.enqueue((stateA_, stateB_))mygraph.addEdge(stateA, stateA_, o)return mygraph, sequence
if __name__ == '__main__':g, sq = solution()# 建立父子关系for v_n in sq:v = g.getVertex(v_n)for nbr in v.getConnections():if nbr.getColor() == 'white':nbr.setPred(v)nbr.setColor('gray')v.setColor('black')target = g.getVertex(sq[-1])# 回溯,显示决策路径while target.getPred():predv = target.getPred()print(target.id, '<--', predv.getWeight(target), '--', predv.id)target = predv

编译结果:

(0, 0, 0) <-- (1, 1) -- (1, 1, 1)
(1, 1, 1) <-- (1, 0) -- (0, 1, 0)
(0, 1, 0) <-- (0, 2) -- (0, 3, 1)
(0, 3, 1) <-- (0, 1) -- (0, 2, 0)
(0, 2, 0) <-- (2, 0) -- (2, 2, 1)
(2, 2, 1) <-- (1, 1) -- (1, 1, 0)
(1, 1, 0) <-- (2, 0) -- (3, 1, 1)
(3, 1, 1) <-- (0, 1) -- (3, 0, 0)
(3, 0, 0) <-- (0, 2) -- (3, 2, 1)
(3, 2, 1) <-- (1, 0) -- (2, 2, 0)
(2, 2, 0) <-- (1, 1) -- (3, 3, 1)

查看sequence后,可以发现,BFS搜索的有效状态其实只有15种,然而在其他博客中(以下)描述有16种有效状态,其中,A岸状态**(0,1,1)**是唯一的不同,why?

sequence记录的状态如下:

(3, 3, 1)
(3, 2, 0)
(2, 2, 0)
(3, 1, 0)
(3, 2, 1)
(3, 0, 0)
(3, 1, 1)
(1, 1, 0)
(2, 2, 1)
(0, 2, 0)
(0, 3, 1)
(0, 1, 0)
(1, 1, 1)
(0, 2, 1)
(0, 0, 0)

原因:BFS搜索到(0,0,0)就停止了,而(0,1,1)是(0,0,0)的跳转状态,所采取的动作为(0,1),即从B岸又送了一只狮子过来,所以理论上这个状态是合法的,但实际搜索时是搜不到的。

参考博客:

https://www.cnblogs.com/guanghe/p/5485800.html

过河问题(图、BFS)相关推荐

  1. CF-1209 F. Koala and Notebook(建图BFS)

    CF-1209 F. Koala and Notebook(建图BFS) 题目链接 题意 n个城市m个双向边,从点1可以到达任何点,把点1到到其他点所经过的边写成一行可以得到一个大数,你的任务使得这个 ...

  2. 图的遍历(C语言,邻接表存储的图 - DFS,邻接矩阵存储的图 - BFS)

    邻接表存储的图 - DFS /* 邻接表存储的图 - DFS */void Visit( Vertex V ) {printf("正在访问顶点%d\n", V); }/* Visi ...

  3. QDUOJ 生化危机 邻接表存图+BFS

    生化危机 发布时间: 2015年10月10日 18:05   时间限制: 1000ms   内存限制: 256M 描述 X博士想造福人类, 研发一种可以再生肢体的药物, 可是很不幸......研究失败 ...

  4. 图Graph--农夫过河问题(BFS/DFS应用)

    农夫过河问题: /*** @description: 农夫过河问题(羊,白菜,狼),一次最多带一个东西过河,* 农夫不在的情况下羊会吃白菜,狼会吃羊,如何平安过河* @author: michael ...

  5. 算法笔记-图--bfs

    题目:PAT A1076 思路: 建立图: dfs便利的的深度不超过给定深度,但是采用BFS 把节点编号和层号建立结构点,控制 #include <iostream> #include & ...

  6. 三牛三虎过河问题--图的最短路径dijkstra算法--简单的Python实现

    问题:三头牛三只虎要过河,船需要一只动物来划,另外至多还能载一物,而只有一头牛和一只虎会划船,并且当虎的数量多于牛的数量时,虎要吃牛,请设计一个安全渡河方案,并使渡河次数尽量少. 我们用一个数组来表示 ...

  7. java数据结构 农夫过河_数据结构笔记分享:18 农夫过河(图的算法运用)

    问题描述 一个农夫带着一只狼,一棵白菜和一只山羊要从一条河的南岸到北岸,农夫每次只能带一样东西过过河,但是任意时刻如果农夫不在场时,狼要吃羊.羊要吃菜,请为农夫设计过河方案. 分析: 要求解农夫过河问 ...

  8. 一一计划(Day 14)邻接表法存储图,BFS广度优先遍历,DFS深度优先遍历

    邻接表法存储 邻接表发存储需要时无权无向图.用数组+链表的方式完成 数组用来记录地点,链表来记录每一个地点对应的相邻地点 1.构造一个数据结构来存放数组的序号以及指针用来指向链表 2.创造结点,构建链 ...

  9. 剑指 Offer II 114. 外星文字典(困难 图 bfs 哈希表 拓扑排序 字符串 数组)

    剑指 Offer II 114. 外星文字典 现有一种使用英语字母的外星文语言,这门语言的字母顺序与英语顺序不同. 给定一个字符串列表 words ,作为这门语言的词典,words 中的字符串已经 按 ...

  10. PAT甲级(Advanced Level)1076(图BFS)

    微博被称为中国版的Twitter.微博上的一个用户可能有很多追随者,也可能关注很多其他用户.因此,一个具有追随者关系的社会网络就形成了.当一个用户在微博上发了一个帖子,他/她的所有追随者都可以查看并转 ...

最新文章

  1. 如此精心整理的深度学习资源只在这里,值得你拥有!(上篇)
  2. jQuery 使用 jQuery UI 部件工厂编写带状态的插件(翻译)
  3. python 合并区间
  4. 冒泡排序(数组)的一种实现
  5. Bfs++ open the lock
  6. Android侧滑删除-RecyclerView轻松实现高效的侧滑菜单
  7. 泰语7个元音变形_泰语发音规则
  8. 随想录(用好自己的时间)
  9. @EnableWebMvc启动springmvc特性
  10. TypeScript算法专题 - blog5 - 单链表节点的`任意k个分组反转`的实现
  11. 0497计算机组成原理在线作业,0497《 综合实践活动课程设计》20秋西南大学在线作业答案...
  12. 标准差和标准误差的区别
  13. 1756冗余_冗余电源1756-PA75R
  14. 饭饭科普47 — 什么是区块链
  15. Unity SteamVR锁定头盔位置旋转
  16. 4.4 使用倾斜工具和整形工具制作图标 [Illustrator CC教程]
  17. 智能机器人编程游戏robocode的运行代码简析
  18. vos3000及外呼系统编码所占带宽详细说明
  19. 有什么好用的网站导航?
  20. 四个技巧提升你的网站百度收录量

热门文章

  1. 如何免费申请idea(学生,教师)教程
  2. CERO二次开发依赖模型参数清单(失败)
  3. 轻松下载各大视频网站的 .Flv 视频
  4. Linux u盘助手总结
  5. aspcms留言增加防刷新验证,单一ip留言条数限制,防止恶意刷新及恶意提交。
  6. swagger-codegen自动生成代码工具的介绍与使用
  7. android webview 加载图片一直显示正在加载中
  8. 收藏!一篇教会你写90%的shell脚本
  9. 怎么把PPT翻译成英文?手把手教你翻译
  10. 软件测试面试八股文——基础篇