传教士和野人问题实验报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.上机内容
传教士与野人问题求解(宽度搜索算法)
二问题背景:
从前有一条河,河的左岸有m个传教士(Missionary)和m个野人(Cannibal),和一艘最多可乘n人的小船。约定左岸,右岸和船上或者没有传教士,或者野人数量少于传教士,否则野人会把传教士吃掉。
三实验内容:
编程,接收m和n,搜索一条可让所有的野人和传教士安全渡到右岸的方案,例如下图:
(M表示传教士(Missionary),C 表示野人(Cannibal))
初态目标
Left Bank River Right
bank
Left
Bank
River Right
bank
M…. M….
C…. C….
注:本实验的举例均以3个传教士和3个野人同在左岸作为初始状态。
四实验方案和算法:
1.数据结构:
本实验需要用到的数据结构主要是队列和堆栈,其实现均包含于dso.h头文件中,分别命名为class stack和class queue。
2.宽度搜索算法:
(1)结点结构:
class Move {
public:
int missionary; //要移动的传教士数量
int cannibal; //野人
};
class ElemType : Move { //继承Move类,获得传教士,野人数据成员。
private:
bool boat; //船是否在左岸?
public:
ElemType* flag; //标示前一状态即扩展出本结点的父结点信息
ElemType(int MAX_PEOPLE) { //创建初始状态
missionary = cannibal = MAX_PEOPLE;
boat = true;
flag = NULL;
}
……
在这里,Elemtype集成了Move,从而获得Move类的传教士和野人数据成员。以上两个类的数据成员用于保存所有扩展生成的结点。其中missionary表示表示左岸上传教士的树目,cannibal表示左岸上野人的树目,boat表示船在哪个岸上(其中true表示在左岸,这是船的初始状态,表示false在右岸), flag 用于标示前一状态即扩展出本结点的父结点信息,以便最后回搜找出解的
路径。
举例说明:假设左岸有3个传教士和3个野人,小船最多可乘2人。把当前左岸的状态抽象为:(3,3,1),前两个"3"代表左岸有3个传教士和3个野人,1代表船在左岸。很明显,目标状态为(0,0,0),表示左岸的传教士和也人数目都是0,而船在右岸。
(2)船的行动规则(successor function):
把每一次可行的渡船方案作为行动规则,用Move类的(m,c)表示。行动规则的两位数分别代表要移动的传教士,野人的数量。对于固定大小的船,其装载能力是一定的,所以它的行动规则空间也是一定的。在这里,用MoveGroup类的构造函数构造出所有的行动规则。
注意,这里MoveGroup类中的Move对象只有500的可用空间,所以,输入的传教士和野人数目构成的行动规则不能超过500种。
(3)宽度优先算法:
实验的主要搜索算法由ANSWER类的构造函数实现,这里主要用到了OPEN和CLOSED队列,以及一个临时的TEMP堆栈。其中,OPEN表用于存放扩展结点,CLOSED表用于存放被扩展结点,TEMP则用于用于记录成功搜索的路径。
搜索过程大致如下描述:先构造初始结点以及船的行动规则,初始结点入OPEN队列,以宽度优先依次搜索OPEN队列头结点的子结点,同时保存受访问结点的父结点信息,知道搜索到目标结点或者无解为止。
算法框图如下所示:
3 程序界面和功能说明:
程序运行环境为DOS控制台,运行开始以后,程序提示输入需要坐船的传教士和野人数目,例如输入3表示有3个传教士和3个野人。
用户按回车键以后,程序继续提示输入船的装载能力,例如输入2表示这个船依次最多可以坐2人。注:一般情况下,船的装载能力应该少于传教士或野人的数量,而且一般为偶数。
程序开始运行时的界面截图:
程序运行结束时的界面截图:
输出文件示例:
Left(3, 3) | (0, 0)Right
[Step 1]
Move 0 missionaries and 2 cannibals to the right side. Left(3, 1) | (0, 2)Right
[Step 2]
Move 0 missionaries and 1 cannibals to the left side. Left(3, 2) | (0, 1)Right
[Step 3]
Move 0 missionaries and 2 cannibals to the right side. Left(3, 0) | (0, 3)Right
[Step 4]
Move 0 missionaries and 1 cannibals to the left side. Left(3, 1) | (0, 2)Right
[Step 5]
Move 2 missionaries and 0 cannibals to the right side. Left(1, 1) | (2, 2)Right
[Step 6]
Move 1 missionaries and 1 cannibals to the left side. Left(2, 2) | (1, 1)Right
[Step 7]
Move 2 missionaries and 0 cannibals to the right side. Left(0, 2) | (3, 1)Right
[Step 8]
Move 0 missionaries and 1 cannibals to the left side. Left(0, 3) | (3, 0)Right
[Step 9]
Move 0 missionaries and 2 cannibals to the right side. Left(0, 1) | (3, 2)Right
[Step 10]
Move 0 missionaries and 1 cannibals to the left side.