传教士问题Word格式文档下载.docx
《传教士问题Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《传教士问题Word格式文档下载.docx(8页珍藏版)》请在冰豆网上搜索。
-3"
表示剩下三个留待最后一次运过去。
除以"
2"
是因为一个来回可以运过去2人,需要[(M+N-3)/2]个来回,而"
来回"
数不能是小数,需要向上取整,这个用符号[]表示。
而乘以"
是因为一个来回相当于两次摆渡,所以要乘以2。
而最后的"
+1"
,则表示将剩下的3个运过去,需要一次摆渡。
化简有:
M+N-2。
再考虑船在右岸的情况。
同样不考虑限制条件。
船在右岸,需要一个人将船运到左岸。
因此对于状态(M,N,0)来说,其所需要的最少摆渡数,相当于船在左岸时状态(M+1,N,1)或(M,N+1,1)所需要的最少摆渡数,再加上第一次将船从右岸送到左岸的一次摆渡数。
因此所需要的最少摆渡数为:
(M+N+1)-2+1。
其中(M+N+1)的"
表示送船回到左岸的那个人,而最后边的"
,表示送船到左岸时的一次摆渡。
(M+N+1)-2+1=M+N。
综合船在左岸和船在右岸两种情况下,所需要的最少摆渡次数用一个式子表示为:
M+N-2B。
其中B=1表示船在左岸,B=0表示船在右岸。
由于该摆渡次数是在不考虑限制条件下,推出的最少所需要的摆渡次数......
从前有一条河,河的左岸有m个传教士(Missionary)和m个野人(Cannibal),和一艘最多可乘n人的小船。
约定左岸,右岸和船上或者没有传教士,或者野人数量少于传教士,否则野人会把传教士吃掉。
编程,接收m和n,搜索一条可让所有的野人和传教士安全渡到右岸的方案。
我们先假设左岸有3个传教士和3个野人,小船最多可乘2人。
把当前左岸的状态抽象为:
(3,3,1)
前两个"
3"
代表左岸有3个传教士和3个野人,1代表船在左岸。
把每一次可行的渡船方案作为算符。
比如,在初始状态,让1个传教士和1个野人上船并渡到右岸,这一算符可表示为:
(1,1)
算符的两位数分别代表要移动的传教士,野人的数量;
把人移到没有船的岸边并且改变状态向量中船的值。
对于固定大小的小船,算符的数量是一定的:
classMove{
public:
intmissionary;
//要移动的传教士数量
intcannibal;
//野人
};
classMoveGroup{
Movemove[500];
//算符集
intnumMove;
//可用算符的总数
MoveGroup(intMAX_ON_BOAT){
//利用构造器求算符集
intm,c,i=0;
for(m=0;
m<
=MAX_ON_BOAT;
m++)
for(c=0;
c<
c++)
if(c==0&
&
m!
=0){
move[i].missionary=m;
move[i].cannibal=0;
i++;
}
elseif(m==0&
c!
move[i].missionary=0;
move[i].cannibal=c;
elseif(m+c<
=MAX_ON_BOAT&
m+c!
=0&
m>
=c){
move[i].missionary=m;
move[i].cannibal=c;
numMove=i;
}
创建一个MoveGroup对象
MoveGroupmg
(2);
即可得到当小船最多可乘2人时的算符集。
这个程序所要做的,就是通过这个已知的算符集,将初始状态(3,3,1)转变为最终状态(0,0,0)。
我们应将状态作为搜索的元素。
构建类时应注意,并不是每个算符对于任意的状态都是可以应用的,这需要对应用算符后的安全性进行检查,以判断这一算符对当前状态是否可用;
同时,类中也要包含一个判断当前状态是否是最终节点(0,0,0)的方法;
当然”==”,”=”这两个运算符以及null值,这是调用dso.h时所不可或缺的。
(详见源文件)
classElemType:
Move{
//继承Move类,获得传教士,野人数据成员。
private:
boolboat;
//船是否在左岸?
ElemType*flag;
//这个后边再说,暂时用不到
ElemType(intMAX_PEOPLE){
//创建初始状态
missionary=cannibal=MAX_PEOPLE;
boat=true;
flag=NULL;
ElemType(){}
booloperator==(ElemTypee){
returnthis->
missionary==e.missionary&
this->
cannibal==e.cannibal&
boat==e.boat;
voidoperator=(ElemTypee){
this->
missionary=e.missionary;
cannibal=e.cannibal;
boat=e.boat;
flag=e.flag;
ElemTypefriendoperator>
>
(ElemTypesource,Move&
mv){
//移动操作,通过重载运算符“>
”,你将在isSafe(ElemType)中见到用法。
ElemTyperesult;
if(source.boat==1){
result.missionary=(source.missionary-=mv.missionary);
result.cannibal=(source.cannibal-=mv.cannibal);
result.boat=0;
}else{
result.missionary=(source.missionary+=mv.missionary);
result.cannibal=(source.cannibal+=mv.cannibal);
result.boat=1;
returnresult;
boolisSafe(Move&
mv,intMAX_PEOPLE){
//判断当前状态在进行mv操作后还是不是安全状态
if((boat==1&
(missionary-mv.missionary<
0||
cannibal-mv.cannibal<
0))||
(boat==0&
(missionary+mv.missionary>
MAX_PEOPLE||
cannibal+mv.cannibal>
MAX_PEOPLE)))
returnfalse;
else{
ElemTypetemp=*this>
mv;
if(temp.missionary==0||temp.missionary==MAX_PEOPLE||
(temp.missionary>
=temp.cannibal&
MAX_PEOPLE-temp.missionary>
=MAX_PEOPLE-temp.cannibal))
returntrue;
elsereturnfalse;
boolisSuccess(){returnmissionary==0&
cannibal==0&
boat==0;
//isSuccess()判断当前状态是否为最终节点
intgetM(){returnmissionary;
intgetC(){returncannibal;
intgetB(){returnboat;
voidprint(){
cout<
<
'
('
missionary<
'
cannibal
boat<
)'
<
endl;
constElemTypenull(0);
//(0,0,1)这是不会出现的
voidprint(ElemType&
e){e.print();
}
//打印函数
至此,我们已经完成了对问题的描述。
搜索过程采用较简单的“宽度优先盲目搜索”,算法框图如下:
#include"
dso.h"
typedefElemTypeStatus;
open,closed表均通过队列实现。
由于对扩展节点要保存指针,所以closed表需要一个获得尾指针的方法。
classQueuex:
publicQueue{
ElemType*getTailPtr(){
if(Queue:
:
isEmpty())returnNULL;
QNode*temp=front->
next;
while(temp!
=rear){
if(temp->
next==rear)return&
temp->
next->
node;
temp=temp->
return&
当得到了一个最终节点(0,0,0)时,如果我们前边的操作没有保存路径的话,那么我们就只知道这个问题有解