传教士过河问题

来源:互联网 发布:淘宝动态评分能改吗 编辑:程序博客网 时间:2024/05/18 02:36

     从前有一条河,河的左岸有m个传教士、m个野人和一艘最多可乘n人的小船。约定左岸,右岸和船上或者没有传教士,或者野人数量不超过传教士,否则野人会把传教士吃掉。搜索可使所有的野人和传教士安全渡到右岸的方案。


在这里给出m=3,c=3,n=2的解析

设置状态变量并确定值域

M为传教士人数,C 为野人人数,B为船数,要求M>=C且M,C<= 3,L表示左岸,R表示右岸。

初始状态                            目标状态

      L   R                                L   R

M   3   0                           M   0   3

C   3   0                            C   0   3

B   1   0                            B   0   1

 

  确定状态组,分别列出初始状态集和目标状态集

用三元组来表示 :(ML , CL , BL)(均为左岸状态)

其中 ,BL ∈{ 0 , 1}

   :(3 , 3 , 1)        : (0 , 0 , 0)

初始状态表示全部成员在河的的左岸;

目标状态表示全部成员从河的左岸全部渡河完毕。

 

定义并确定规则集合

仍然以河的左岸为基点来考虑,把船从左岸划向右岸定义为Pij操作。其中,第一下标i表示船载的传教士数,第二下标j表示船载的食人者数;同理,从右岸将船划回左岸称之为Qij操作,下标的定义同前。则共有10种操作,操作集为

 F={P01,P10,P11,P02,P20,Q01,Q10,Q11,Q02,Q20}

P10  if ( ML ,CL , BL=1 )   then ( ML–1 , CL , BL –1 )

P01  if ( ML ,CL , BL=1 )   then ( ML , CL–1 , BL –1 )

P11  if ( ML ,CL , BL=1 )   then ( ML–1 , CL–1 , BL –1 )

P20  if ( ML ,CL , BL=1 )   then ( ML–2 , CL , BL –1 )

P02  if ( ML ,CL , BL=1 )   then ( ML , CL–2 , BL –1 )

Q10   if ( ML ,CL , BL=0 )   then ( ML+1 , CL , BL+1 )

Q01  if ( ML ,CL , BL=0 )   then ( ML , CL+1 , BL +1 )

Q11  if ( ML ,CL , BL=0 )   then ( ML+1 , CL +1, BL +1 )

Q20    if ( ML ,CL , BL=0 )   then ( ML+2 , CL +2, BL +1 )

Q02  if ( ML ,CL , BL=0 )   then ( ML , CL +2, BL +1 )

  画出合理的状态空间图

                                                                                   

图1 状态空间图

箭头旁边所标的数字表示了P或Q操作的下标,即分别表示船载的传教士数和食人者数。

这里我使用了深度优先的搜索方法。


#include <iostream>
#include <string>
#include <vector>


using namespace std;


int BOAT=1;   //0表示在右岸,1表示在左岸
int TIMES = 1; //记录总共的步数
int NUM_B;    //定义船能乘坐的人数


vector <string> fangwen;


bool SAVE(int ML,int CL,int MR,int CR,int BOAT)
{
char ch[20];
if (CL < 0 || ML < 0 || MR < 0 || CR < 0)   return false;   //如果一方人数小于0时返回 false
if ((ML&&CL > ML) || (MR&&CR>MR))           return false;   //传教士人数小于野人会被吃掉
if ((BOAT == 0 && CL == 0 && ML == 0))                          //左岸传教士和野人都为零的时候成功渡河
{
cout <<"第"<< TIMES <<"条路。"<< endl;
sprintf(ch, "0,0,0");
fangwen.push_back(ch);           //将路径输出
for (int i = 0; i < fangwen.size(); i++)
{
cout << fangwen[i] << endl;
}
cout << endl << endl << endl;
fangwen.pop_back();
TIMES++;
return true;
}


if (BOAT) sprintf(ch, "%d,%d,1", ML, CL);  //记录走过的点
else sprintf(ch, "%d,%d,0", ML, CL);    //记录走过的点


for (int i = 0; i < fangwen.size(); i++)   //防止在死循环 如(1,1,1)||(1,1,0) 来来去去
  {
if (fangwen[i] == ch)
return false;
}
fangwen.push_back(ch); 


if (BOAT)
{
/*当船只能乘坐两个人的时候
(SAVE(ML - 2, CL, MR + 2, CR, 1 - BOAT));
(SAVE(ML-1, CL-1, MR+1, CR+1, 1 - BOAT));
(SAVE(ML, CL-2, MR, CR+2, 1 - BOAT));
(SAVE(ML-1, CL, MR+1, CR, 1 - BOAT));
(SAVE(ML, CL-1, MR, CR+1, 1 - BOAT));
*/


for (int a = 0; a <=NUM_B; a++)
{
for (int b = 0; b <=NUM_B - a; b++)
{
if(a>0||b>0)  //总要有人开船不是
(SAVE(ML-a, CL - b, MR+a, CR + b, 1 - BOAT));
}
}


}
else
{
   /* /*当船只能乘坐两个人的时候
(SAVE(ML, CL+2, MR, CR-2, 1 - BOAT));
        (SAVE(ML+1, CL+1, MR-1, CR-1, 1 - BOAT));
(SAVE(ML, CL+1, MR, CR-1, 1 - BOAT));
(SAVE(ML+1, CL, MR-1, CR, 1 - BOAT));
(SAVE(ML+2, CL, MR-2, CR, 1 - BOAT));
*/
for (int a = 0; a <= NUM_B; a++)   
{
for (int b = 0; b <= NUM_B - a; b++)
{
if (a>0 || b>0)    //总要有人开船不是
(SAVE(ML + a, CL + b, MR - a, CR - b, 1 - BOAT)); 
}
}

}
fangwen.pop_back();
return false;
}


int main()
{
string ch;
int M, C;
cout << "请输入传教士的人数:"; cin >> M;
cout << "请输入野人的人数:"; cin >> C;
cout << "请输入船能乘坐的人数:"; cin >> NUM_B;


if (!SAVE(M, C, 0, 0, 1)&&TIMES<=1)
{
cout << "无路可寻!!" << endl;
}
}


原创粉丝点击