问题描述:
【传教士和食人者问题】
在河的左岸有3个传教士、1条船和3个食人者,传教士们想用这条船将所有的成员运到河的右岸,但是受到以下条件的限制:
(1)船每次最多只能装2个乘客(传教士和食人者都会划船);
(2)在任何岸边,如果食人者数目超过传教士则传教士将被食人者吃掉。
(3)假定食人者会服从任何一种过河安排。
解题思路:
这里个是找到一种过河安排,所以这里用DFS比较合适(这里用BFS的话最终达到目标状态但是,对于目标状态的父亲节点是比较难找的),这里我的空间状态的表示,是看了魏宇轩大佬写的博客后使用的,这里是使用一个结构体
struct zt
{
int lm;
int lc;
int rm;
int rc;
int ship;
};
这里面lm代表着A岸的传教士,lc代表着A岸的野人,rm代表着B岸的传教士,rc代表着B岸的野人,ship表示小船的状态(1表示在A岸,-1表示在B岸);这样整个A,B岸分布的传教士、野人以及船的状态都很清楚的表示出来。初始状态(3,3,0,0,1)到目标状态(0,0,3,3,-1)
int xx[]={0,0,1,1,2};//每次可以上船的人员数量
int yy[]={1,2,0,1,0};
这两个数组代表着每次上几个传教士和野人到小船上(xx表示传教士,yy表示野人);一共只有五种情况,并且不存在小船上野人大于食人者
int Isdangerous(zt temp)
{
if((temp.lm>=temp.lc&&temp.rm>=temp.rc&&temp.lm>=0&&temp.lc>=0&&temp.rm>=0&&temp.rc>=0)||
(temp.lm>=0&&temp.lc>=0&&temp.rm>=0&&temp.rc>=0&&temp.lm==0&&temp.rm>=temp.rc)||
(temp.lm>=0&&temp.lc>=0&&temp.rm>=0&&temp.rc>=0&&temp.lm>=temp.lc&&temp.rm==0))
{
return 1;
}
return 0;
}
这个函数是危险判断:一般大家都会知道每一个岸上的传教士人数都要大于等于野人的人数,还有就是如果一个岸上仅有野人没有传教士这样的情况也是符合条件的(note:当一个岸上的存在没有传教士时,另外一个岸上就一定有传教士并且数量需要大于野人数)
int pd(zt p,int top)
{
for(int i=0;i<top;i++)
{
if(temp[i].lm==p.lm&&temp[i].lc==p.lc&&temp[i].rm==p.rm&&temp[i].rc==p.rc&&temp[i].ship==p.ship)
{
return 0;
}
}
return 1;
}
这个函数是用来判断当前状态是否之前以及存在了;(所有的状态都用一个结构体数组保存起来)
struct zt temp[1000];//结构体数组
int top=0;
结构体数组,用来存储空间状态的数组,top为指针;
完整代码:
C++:
#include
#include
using namespace std;
int xx[]={0,0,1,1,2};//每次可以上船的人员数量
int yy[]={1,2,0,1,0};
int all=0;
struct zt
{
int lm;
int lc;
int rm;
int rc;
int ship;
};
struct zt temp[1000];//结构体数组
int top=0;
int Isdangerous(zt temp)
{
if((temp.lm>=temp.lc&&temp.rm>=temp.rc&&temp.lm>=0&&temp.lc>=0&&temp.rm>=0&&temp.rc>=0)||
(temp.lm>=0&&temp.lc>=0&&temp.rm>=0&&temp.rc>=0&&temp.lm==0&&temp.rm>=temp.rc)||
(temp.lm>=0&&temp.lc>=0&&temp.rm>=0&&temp.rc>=0&&temp.lm>=temp.lc&&temp.rm==0))
{
return 1;
}
return 0;
}
int pd(zt p,int top)
{
for(int i=0;i<top;i++)
{
if(temp[i].lm==p.lm&&temp[i].lc==p.lc&&temp[i].rm==p.rm&&temp[i].rc==p.rc&&temp[i].ship==p.ship)
{
return 0;
}
}
return 1;
}
void dfs(zt p)//DFS遍历从初始状态(3,3,0,0,1)目标(0,0,3,3,-1)
{
if(p.lm==0&&p.lc==0&&p.rm==3&&p.rc==3)
{
all++;
cout<<"这是第"<<all<<"种方案"<<endl;
cout<<"初始状态"<<temp[0].lm<<' '<<temp[0].lc<<' '<<temp[0].rm<<' '<<temp[0].rc<<endl;
for(int i=1;i<top;i++)
{
if(i%2!=0)
{
cout<<"A岸------->B岸:"<<"承载"<<abs(temp[i].rm-temp[i-1].rm)<<"个传教士 "<<abs(temp[i].rc-temp[i-1].rc)<<"个食人者"<<temp[i].lm<<' '<<temp[i].lc<<' '<<temp[i].rm<<' '<<temp[i].rc<<endl;
}
if(i%2==0)
{
cout<<"B岸------->A岸:"<<"承载"<<abs(temp[i].rm-temp[i-1].rm)<<"个传教士 "<<abs(temp[i].rc-temp[i-1].rc)<<"个食人者"<<temp[i].lm<<' '<<temp[i].lc<<' '<<temp[i].rm<<' '<<temp[i].rc<<endl;
}
}
cout<<endl;
}
for(int i=0;i<5;i++)
{
int x=xx[i];
int y=yy[i];
zt temp1;
if(p.ship==1)//船在A岸时
{
temp1.lm=p.lm-x;
temp1.lc=p.lc-y;
temp1.rm=p.rm+x;
temp1.rc=p.rc+y;
temp1.ship=-1;
if(Isdangerous(temp1)==1&&pd(temp1,top)==1)
{
temp[top++]=temp1;
dfs(temp1);
top--;//回溯状态
}
}
if(p.ship==-1)=//船在B岸
{
temp1.lm=p.lm+x;
temp1.lc=p.lc+y;
temp1.rm=p.rm-x;
temp1.rc=p.rc-y;
temp1.ship=1;
if(Isdangerous(temp1)==1&&pd(temp1,top)==1)
{
temp[top++]=temp1;
dfs(temp1);
top--;//回溯到上一个状态
}
}
}
}
int main()
{
zt strat;
strat.lm=3;
strat.lc=3;
strat.rm=0;
strat.rc=0;
strat.ship=1;
temp[top++]=strat;
dfs(strat);
cout<<"一共有: "<<all<<"种方案"<<endl;
return 0;
}
python版本:思路和上面一样,只是在python里面使用的是class类来表示状态空间;
class zt:
def __init__(self,x1,y1,x2,y2,ship):
self.lm=x1
self.lc=y1
self.rm=x2
self.rc=y2
self.ship=ship
def Isdangerous(temp):
if (( temp.lm >= temp.lc and temp.rm >= temp.rc and temp.lm >= 0 and temp.lc >= 0 and temp.rm >= 0 and temp.rc >= 0)
or (temp.lm >= 0 and temp.lc >= 0and temp.rm >= 0 and temp.rc >= 0 and temp.lm == 0 and temp.rm >= temp.rc)
or (temp.lm >= 0 and temp.lc >= 0 and temp.rm >= 0 and temp.rc >= 0 and temp.lm >= temp.lc and temp.rm == 0)):
return 1
else:
return 0
def pd(p,index):
for i in range(0,index):
if(list[i].lm==p.lm and list[i].lc==p.lc and list[i].rm==p.rm and list[i].rc==p.rc and list[i].ship==p.ship):
return 0
return 1
def fin(temp):
if temp.lm==0 and temp.lc==0 and temp.rm==3 and temp.rc==3:
return 1
return 0
xx=[0,0,1,1,2]
yy=[1,2,0,1,0]
list=[]#对象list
index=0#对象指针
sum=0
def dfs(p):
global index,sum
if fin(p)==1:
sum+=1
print("找到了第%d种方案"%sum)
for i in range(0,index+1):
print(list[i].lm,list[i].lc,list[i].rm,list[i].rc)
for i in range(0,5):
x=xx[i]
y=yy[i]
if p.ship==1:
temp1=zt(p.lm-x,p.lc-y,p.rm+x,p.rc+y,-1)
# print(temp1.lm,temp1.lc,temp1.rm,temp1.rc,end=' ')
if Isdangerous(temp1)==1 and pd(temp1,index)==1:
list.append(temp1)
index+=1
dfs(temp1)
index-=1
else:
temp1 = zt(p.lm + x, p.lc + y, p.rm -x, p.rc -y, 1)
if Isdangerous(temp1) == 1 and pd(temp1, index) == 1:
list.append(temp1)
index += 1
dfs(temp1)
index -= 1
start=zt(3,3,0,0,1)
list.append(start)
index+=1
dfs(start)
print("一共找到了%d种方案"%sum)