任何时刻,如果有野人和传教士在一起,必须要求传教士的人数多于或等于野人的人数。
设M为传教士的人数,C为野人的人数,用状态空间发求解此问题的过程如下:
M、C=N,boat=k,要求M>=C且M+C<=K初始状态目标状态
LRLR
M30M03
C30C03
B10B01
(1)用三元组来表示(ML,CL,BL)
其中0<=ML,CL<=3,BL?
{0,1}
(3,3,1)(0,0,0)
(2)规则集合
Pif(ML,CL,BL=1)then(ML–1,CL,BL–1)10
Pif(ML,CL,BL=1)then(ML,CL–1,BL–1)01
Pif(ML,CL,BL=1)then(ML–1,CL–1,BL–1)11
Pif(ML,CL,BL=1)then(ML–2,CL,BL–1)20
Pif(ML,CL,BL=1)then(ML,CL–2,BL–1)02
Qif(ML,CL,BL=0)then(ML+1,CL,BL+1)10
Qif(ML,CL,BL=0)then(ML,CL+1,BL+1)01
Qif(ML,CL,BL=0)then(ML+1,CL+1,BL+1)11
Qif(ML,CL,BL=0)then(ML+2,CL+2,BL+1)20
Qif(ML,CL,BL=0)then(ML,CL+2,BL+1)02
(3)寻找一个启发式函数引导规则的选用
右岸总人数6–ML–CL两岸中传教士数目>=野人数目f=
–?
其它
(3,3,1)
f=2P110102f=1Pf=2P
(2,2,0)(3,2,0)(3,1,0)
f=1Q01f=1Q11
(3,2,1)
f=3P02(3,0,0)
f=2Q01
(3,1,1)
f=4P20
(1,1,0)
f=2Q11(2,2,1)
f=4P20
(1,1,0)
f=2Q11
(2,2,1)1
f=4P20(0,2,0)
f=3Q01
(0,3,1)
f=5P02
(0,1,1)f=4Q0110f=4Q
(1,1,1)(0,2,1)
f=3Q01f=3Q01(0,0,0)
6.2.3用状态空间法求解传教士和食人者问题
传教士和食人者问题(TheMissionariesandCannibalsProblem)。
在河的左岸有3个传教士、1条船和3个食人者,传教士们想用这条船将所有的成员运过河去,但是受到以下条件的限制:
(1)传教士和食人者都会划船,但船一次最多只能装运两个;
(2)在任何岸边食人者数目都不得超过传教士,否则传教士就会遭遇危险:
被食人者攻击甚至被吃掉。
此外,假定食人者会服从任何一种过河安排,试规划出一个确保全部成员安全过河的计划。
解我们按上述步骤来进行求解分析。
(1)设定状态变量及确定值域。
为了建立这个问题的状态空间,设左岸的传教士数为m,则有m={0,1,2,3};
对应右岸的传教士数为3—m;左岸的食人者数为c,则有c={0,1,2,3};
对应右岸食人者数为3—c;左岸船数为b,故又有b={0,1};右岸的船数为1-b。
(2)确定状态组,分别列出初始状态集和目标状态集。
问题的状态可以用一个三元数组来描述,以左岸的状态来标记,即右岸的状态可以不必标出。
Sk=(m,c,b)
初始状态只有一个:
S0=(3,3,1),初始状态表示全部成员在河的的左岸;
目标状态也只有一个:
Sg=(0,0,0),表示全部成员从河的左岸全部渡河完毕。
(3)定义并确定操作集。
仍然以河的左岸为基点来考虑,把船从左岸划向右岸定义为Pij操作。
其中,第一下标i表示船载的传教士数,第二下标j表示船载的食人者数;同理,从右岸将船划回左岸称之为Qij操作,下标的定义同前。
则共有10种操作,操作集为
F={P01,P10,P11,P02,P20,Q01,Q10,Q11,Q02,Q20}
(4)估计全部的状态空间数,并尽可能列出全部的状态空间或予以描述。
在这个问题世界中,S={3,3,1}为初始状态,S=Sg=(0,0,0)为目标状态。
031
全部的可能状态共有32个,如表6—1所示。
表6—1传教士和食人者问题的全部可能状态
状态m,c,b状态m,c,b状态m,c,b状态m,c,bS03,3,1S81,3,1S163,3,0S241,3,0S13,2,1S91,2,1S173,2,0S251,2,0S23,1,1S101,1,1S183,1,0S261,1,0S33,0,1S111,0,1S193,0,0S271,0,0S42,3,1S120,3,1S202,3,0S280,3,0S52,2,1S130,2,1S212,2,0S290,2,0S62,1,1S140,1,1S222,1,0S300,1,0S72,0,1S150,0,1S232,0,0S310,0,0
值得注意的是按照题目规定的条件,我们应该划去不合法的状态,这样可以加快搜索求解的效率。
例如,首先可以划去岸边食人者数目超过传教士的情况,即,4、S8、S9、S20、S24、S25等6种状态是不合法的;其次,应该划去右岸边食人者数目超过修道士的情况,即S6、S7、S11、S22、S23、S27等情况;余下20种合法状态中,又有4种是不可能出现的状态;S15和S16不可能出现,因为船不可能停靠在无人的岸边;S3不可能出现,因为传教士不可能在数量占优势的食人者眼皮底下把船安全地划回来;还应该划去S28,因为传教士也不可能在数量占优势的食人者眼皮底下把船安全地划向对岸。
可见,在状态空间中,真正符合题目规定条件的只有16个合理状态。
(5)当状态数量不是很大时,按问题的有序元组画出状态空间图,依照状态空间图
搜索求解。
根据上述分析,共有16个合法状态和允许的操作,可以划出传教士和食人者问题的状
态空间图,如图6—4所示。
S10SS1812S24
31011003111102011120011002S11SSS14171290101
020000331320321311010011
S13SSS023020S0102S021321011011300221220021
SS195
图6—4传教士和食人者问题的状态空间
如图6—4所示,由于划船操作是可逆的,所以图中状态节点间用双向箭头连接,箭头旁边所标的数字表示了,或,操作的下标,即分别表示船载的传教士数和食人者数。
这样,任何一条从S0到达S31的路径都是该问题的解。
这样,通过运用状态空间表示法就解决了传教士和食人者问题的求解。
源代码:
#include
#include
#include
#definemaxloop100/*最大层数,对于不同的扩展方法自动调整取值*/#definepristnum3/*初始化时设定有3个野人3个传教士,实际可以改动*/#defineslavenum3
structSPQ
{intsr,pr;/*船运行一个来回后河右岸的野人、传教士的人数*/
intsl,pl;/*船运行一个来回后河左岸的野人、传教士的人数*/
intssr,spr;/*回来(由左向右时)船上的人数*/
intsst,spt;/*去时(由右向左时)船上的人数*/
intloop;/*本结点所在的层数*/
structSPQ*upnode,*nextnode;/*本结点的父结点和同层的下一个结点的地址*/
}spq;
intloopnum;/*记录总的扩展次数*/
intopenednum;/*记录已扩展节点个数*/
intunopenednum;/*记录待扩展节点个数*/
intresultnum;
structSPQ*opened;
structSPQ*oend;
structSPQ*unopened;
structSPQ*uend;
structSPQ*result;
voidinitiate();
voidreleasemem();
voidshowresult();
voidaddtoopened(structSPQ*ntx);
intsearch();
voidgoon();
intstretch(structSPQ*ntx);
voidrecorder();
voidaddtoopened(structSPQ*ntx)/*扩展节点*/
{
unopened=unopened->nextnode;
unopenednum--;
if(openednum==0)
oend=opened=ntx;
oend->nextnode=ntx;
oend=ntx;
openednum++;
}
voidrecorder()
{
inti,loop;
structSPQ*newnode;
structSPQ*ntx;
loop=oend->loop;
ntx=oend;
resultnum=0;
for(i=0;i<=loop;i++)
{
newnode=(structSPQ*)malloc(sizeof(spq));
if(newnode==NULL)
{
printf("\n内存不够~\n");
exit(0);
}
newnode->sr=ntx->sr;
newnode->pr=ntx->pr;
newnode->sl=ntx->sl;
newnode->pl=ntx->pl;
newnode->sst=ntx->sst;
newnode->spt=ntx->spt;
newnode->ssr=ntx->ssr;
newnode->spr=ntx->spr;
newnode->nextnode=NULL;
ntx=ntx->upnode;
if(i==0)
result=newnode;
newnode->nextnode=result;
re