问题描述

  有 N 个传教士和 N 个野人来到河边渡河,河岸有一条船,每次至多可供 k 人乘渡。问:传教士为了安全起见,应如何规划摆渡方案,使得任何时刻, 河两岸以及船上的野人数目总是不超过传教士的数目(否则不安全,传教士有可能被野人吃掉)。 即求解传教士和野人从左岸全部摆渡到右岸的过程中,任何时刻满足 M (传教土数) ≥ C 野人数)和 M+C≤k 的摆渡方案。

写在前面

传教士与野人过河问题是人工智能里面非常经典的算法题,曾经是2012年360公司的面试题,因此网上有各种各样的解决思路和代码设计,但是我发现网上的算法思路非常好,代码设计的尽管多种多样,但都不满足我今天这个要做的这个项目需求,大部分都是规定好了3个传教士和3个野人,船上最多只能做2人,或者是先按照3人举例,举例完就没了。这样的限制是不符合今天这种需求的,因为今天的问题描述中并没有说明会有几个传教士和野人,只能保证传教士和野人一般多,也没有说明一条船最多做几个人,因此网上大部分代码案例不合题。再有一种就是允许我们输入有多少人和船最多坐多少人,但是他没有找出所有的方案。所以今天就自己动手做一下这个算法。

算法思路

  这个算法很经典,网上有很多的解题思路,我也看了很多,有一个感觉讲的非常不错的推荐一下,就是 CSDN-魏宇轩 前辈的博文,讲解的非常棒,思路很清晰,而且他还使用 C语言写了出来,但是我运行出了写问题一直没有搞通,于是自己用 C# 写了一个,其中算法设计思路是按照 魏宇轩 前辈的思路走的。

算法分析

参考各3人渡河,船最多载2人的算法思路。最终项目人数可以自定义。

  初始状态:河左岸有3个野人河3个传教士;河右岸有0个野人和0个传教士;船停在左岸,船上有0个人。
  目标状态:河左岸有0个野人和0个传教士;河右岸有3个野人和3个传教士;船停在右岸,船上有0个人。
  将整个问题抽象成怎样从初始状态经一系列的中间状态从而达到目标状态,状态的改变是通过划船渡河来引发的。
  根据要求,共得出以下5中可能的渡河方案:
    (1)渡2传教士
    (2)渡2野人
    (3)渡1野人1传教士
    (4)渡1传教士
    (5)渡1野人
  本程序使用类来定义状态结点,使用集合存储状态结点,使用递归的思想来寻找目标状态。

程序详细执行流程如下

  首先,包含状态(首次为初始状态)的结构体结点(已存入结构体数组)传入处理函数,然后判断该传入结点状态是否为目标状态。

是则遍历打印结构体数组,打印完成之后,返回递归调用处,顺序执行之后代码(此步骤关系到是否能找到所有过河路径);

否则继续判断是否该传入结点已存在于结构体数组当中,如存在,不再往下执行,返回递归调用处,顺序执行之后代码;

若不存在,则继续判断该传入状态的人数是否合理(是否出现人物数量小于0的情况等),若不合理,返回递归调用处,顺序执行之后代码;

若合理,则继续判断传教士和野人人数限制条件,即在传教士人数不为0的情况下,野人人数是否大于传教士人数,若大于则出现吃人的情况,也就是说该传入状态也不合理,则返回递归调用处,顺序执行之后代码;

若不满足大于条件,则说明该状态是路径转态,也就是合理的,那么进行五种渡河方案的依次变换,首先为第一种渡河方案,两个传教士过河(注意:此处的5中渡河方案没有固定顺序,也可以是其他渡河方案),那么对该传入状态的左岸和右岸的传教士人数和野人人数进行增减(若为左岸到右岸,则左岸人数减,右岸人数加,此处有一个小技巧见本段末尾)。

增减完成并改变船的状态(使用正负一表示,正一为左岸,负一为右岸)以后就产生了一个新的状态,将该状态存入结构体数组,之后此处又递归调用处理函数,将新产生的转态结点传入,再次进行上述条件限制判断。

若在该判断途中被返回至递归调用处,说明该状态不合理,则此时将已经存入结构体数组的状态结点移出结构体数组,然后程序顺序执行,进行下一个渡河方案的处理,也就是说,此时的处理是对上一个传入结点的操作(因为刚传入的已经移出了);

若在判断途中未被返回至递归调用处,也就是说,传入的结点合理了,那么又开始从第一种渡河方案开始对该传入状态进行操作。

按照上述过程循环执行,直到出现目标状态,回到本段开头,遍历结构体数组,打印渡河路径结点信息。

完成以后,返回递归调用处,顺序执行之后代码,此后的操作是在寻找其他渡河路径。

原理为:由于该处理函数末尾存在return语句(关键),所以在找到目标状态并返回之后,目标转态结点同样会被移出结构体数组,然后在其上一个结点开始顺序往下执行操作之后的一种渡河方案,查看是否在该结点处,还有其他渡河方案可以达到目标状态,若有则同样按上述方法执行(打印输出),若执行完后面的所有渡河方案,发现都没有能够达到目标状态的结点,则会执行末尾的返回语句,返回之后,该状态结点也会被移除(关键),那么此时操作的状态结点就是上上个结点状态,对其进行其后的渡河方案操作。按照此法,不断往后退,直到所有结点都被移除,此时说明已经完成所有渡河路径的搜索(深度)。至此,本程序的执行过程叙述完毕。

  小技巧:从左岸到右岸,和从右岸到左岸的状态变化是不一样的,前者左岸的人数减,右岸的人数加;后者左岸的人数加,右岸的人数减。我们不应单独再写程序来处理,而是应该使用船的转态带入计算来处理,注意,此技巧在于船的转态使用正负一来表示,而不应该是1和0,以及其他表示方法。为什么这么说?因为任何数乘以一,其本身都不会改变(有我也不会承认)。而正负号在此起到关键作用,我们使用正负一去乘以五种渡河方案的改变数值,从而得到的就是我们变换的正确结果,不论左岸右岸,都是正确合理的。

项目关键代码

主要用来判断这条方案是否可行。

           // 是否重复操作for (int i = 0; i < index; i++){if (m.left_c == m_fun[i].left_c && m.left_y == m_fun[i].left_y){if (m.boat_location == m_fun[i].boat_location){return 0;}}}// 人数是否合理if (m.left_c < 0 || m.left_y < 0 || m.right_c < 0 || m.right_y < 0){return 0;}// 传教士的人数是否大于等于野人if ((m.left_c < m.left_y && m.left_c != 0) || (m.right_c < m.right_y && m.right_c != 0)){return 0;}

递归算法,主要是用来计算船的渡河载客可能性。

           // 递归算法for (int cchuan = chuan; cchuan >= 0; cchuan-- ){for (int ychuan = chuan; ychuan >= 0 ; ychuan--){if ((cchuan >= ychuan && cchuan + ychuan <= chuan && cchuan + ychuan>0) || (cchuan < ychuan && cchuan == 0)){mm.left_c = m.left_c - cchuan * m.boat_location;mm.left_y = m.left_y - ychuan * m.boat_location;mm.right_c = m.right_c + cchuan * m.boat_location;mm.right_y = m.right_y + ychuan * m.boat_location;mm.boat_location = (-m.boat_location);index = index + 1;m_fun.Insert(index, mm);mCalculation(m_fun[index]);index = index - 1;}}}

最终效果

界面打开,默认传教士和野人的渡河人数都是3人,船的最大载客量为2人,可以自己修改。

  

设置完人数后,点击“开始计算渡河方案”按钮,开始计算所有可行方案。

  

【问题】

  根据设置人数和最大载人量的不同,算法可执行方案大不同,因为代码中并没有使用多线程,在计算过程之中会存在运算数据过大,计算时间过长,在计算的过程当中出现程序界面卡死,直到利用足够的时间计算完成为止。

资料参考自: CSDN-魏宇轩 :https://blog.csdn.net/qq_36260974/article/details/84404168

项目代码

https://github.com/wjw1014/CrossingtheRiver (仅供参考)

传教士与野人过河问题 人工智能实验算法相关推荐

  1. 传教士与野人过河问题(A*搜索 C++)

    传教士与野人过河问题: 任意时刻,左岸.右岸.船上如果传教士人数少于野人人数,传教士就会被野人吃掉.当然野人会划船.传教士人数为0也是可以的. 启发函数 f=g+h.  g当前结点所在解空间树的深度. ...

  2. 野人与传教士过河java_传教士和野人过河(经典MC问题)

    这个问题本来是<人工智能技术导论>第三章的课后题,今天上午考试正巧考到了这道题,要我们画状态转换图,我之前思考过一点,所以写出的状态表示应该没有问题,但这些状态太多了.......,十来种 ...

  3. JAVA野人_Java实现传教士与野人过河问题

    1  问题定义 河的两岸有三个传教士和三个野人需要过河,目前只有一条能装下两个人的船,在河的任何一方或者船上,如果野人的人数大于传教士的人数,那么传教士就会被野人攻击,怎么找出一种安全的渡河方案呢? ...

  4. 人工智能:传教士与野人过河(BFS-基于对列和链表的实现)c++

    传教士(牧师)与野人问题 问题描述: 有n个牧师和n个野人准备渡河,但只有一条能容纳c个人的小船,为了防止野人侵犯牧师,要求无论在何处,牧师的人数不得少于野人的人数(除非牧师人数为0),且假定野人与牧 ...

  5. 【C++】人工智能实验一 猴子摘香蕉/传教士与野人(含完整代码与状态迁移图)

    文章目录 一.猴子摘香蕉问题 1.问题描述 2.解题思路 3.实验结果及分析 实验结果一 实验结果二 实验结果三 4.实验结果 5.实验代码 二.传教士(牧师)与野人问题 1.问题描述 2.实验步骤 ...

  6. 人工智能实验二——prolog语言求解渡河问题(传教士和野人渡河,农夫渡河问题)实现详解

    农夫渡河问题求解 这两个问题都是渡河问题,思路和方式是一样的:给出求解Prolog代码: 问题描述 一个农夫带着一匹狼.一只羊.一颗白菜要过河, 只有一条船而且 农夫每次最多只能带一个动物或物品过河, ...

  7. 修道士和野人过河问题 A*算法 人工智能

    /** * 2014-08-25 by Liy * 修道士和野人过河问题,一共有3个修道士和3个野人,1条船 * 1.船最多可乘坐2人 * 2.两岸边 野人的数量不能多于修道士的数量,否则修道士会被吃 ...

  8. C语言版,传教士与野人渡河问题,使用深度优先搜索法求解(DFS),变态版,随便输入人数和船的最大载人数,人工智能经典题目,简单易懂,注释到位,没有bug

    目录 一.问题描述 二.迟来的代码 运行截图 三.简单分析 一.问题描述 有n个传教士和n个野人准备渡河,但只有一条能容纳c个人的小船,为了防止野人侵犯传教士,要求无论在何处,传教士的人数不得少于野人 ...

  9. 人工智能--野人过河

    课程简介 人工智能(Artificial Intelligence),英文缩写为AI.它是研究.开发用于模拟.延伸和扩展人的智能的理论.方法.技术及应用系统的一门新的技术科学.人工智能的定义可以分为两 ...

最新文章

  1. Java8Stream
  2. 计算机室是学校重要的教学设施,计算机室治理规章.doc
  3. LeetCode算法入门- Longest Substring Without Repeating Characters-day4
  4. 桃李不言,下自成蹊——《大数据》编辑部祝各位老师节日快乐!
  5. 一个简单的完成端口(服务端/客户端)类
  6. linux 内存管理 ppt,Linux内存管理 Memory Manager.ppt
  7. 默认栅格大小为多少_用于创建空栅格的ST_MakeEmptyRaster函数
  8. 使用类名称创建实例并调用构造函数
  9. python基础代码大全-【01】Python基础入门 (全)
  10. 2020年全球激光雷达行业竞争格局分析,技术路线正处于快速发展迭代阶段「图」
  11. Audio Ease Indoor 混响插件评测
  12. 网络ip段计算,网络地址计算,广播地址计算,主机号计算规则
  13. 注册微信小程序账号 APPID的获取
  14. android7.1添加F13到F24按键
  15. 一键查询 | 2020年最新SCI期刊影响因子报告
  16. html背景音乐怎么连续,如何在主页中制作连续的背景音乐_html网页培训
  17. 《江苏省ITS体系框架与规划——需求分析子课题》工作大纲评审会在南京举行[转贴,出处:ITSC 作者:刘浩,张可]
  18. HCIP-DATACOM H12-831(41-60)
  19. 智慧出击,浪潮云海为海上风电打个样
  20. AI上推荐 之 SDM模型(建模用户长短期兴趣的Match模型)

热门文章

  1. 高考还能够改变普通人的命运么?数据分析来告诉你!!
  2. 用java编写一个美元汇率转换,编写一个人民币和美元的双向汇率转换程序,实现美元和人民币转换,汇率:1美元=6.92人民币...
  3. 性别符号php,树也分男女?给6万棵杨树画上性别符号,原来是因为…
  4. h5游戏抽奖游戏源码_抽奖扑克游戏
  5. Ubuntu18.04使用Carla导入RoadRunner自建地图并使用
  6. [附源码]java毕业设计网上书店系统
  7. 企业管理 史玉柱:公司只有三个人可以谈战略,其他人抓好执行
  8. polkit启动失败_CentOS Linux 7.4中polkit服务启动失败
  9. java 补齐字符串_使用String.format()格式化字符串,java自动补全自增长字符串
  10. Java 金额信息存储、精度运算及显示的处理