请将下面这个例子设计成一个状态搜索系统,(1)分析采用宽度优先和深度优先方法分别进行搜索,写出搜索的过程及搜索路径(可以画图来分析)(2)采用C语言或Python编程实现(代码中适当加注释,输出具有可读性)。
问题描述:设有m个传教士和n个野人来到河边,打算乘一只船从左岸渡到右岸去,该船每次最多载3人。在任何时候,如果野人人数超过传教士人数,那么野人就会把传教士吃掉。他们怎样才能用这条船安全地把所有人都渡河过去?试采用宽带优先和深度优先方法分析搜索过程。(说明:传教士和野人都会划船,测试:m=n=3)
思路:
首先肯定要建立一个操作集(存储所有操作算子)和目前状态变量(并附上一个bool函数判断此时状态是否可以进行某些操作)以及一个变量存储是往左走还是往右走,用一个vector存储所有走过的状态,深度遍历时按照不同操作往里面push元素并递归到下一状态里,若遇到所有操作不可行,则pop掉这个元素。而这个状态变量如何定义呢?初设定为(al,bl,ar,br),其中分别为传教士、野人在左右两岸的数量,那么二者在船上的数量可以通过m-al-ar,n-bl-br来计算。
代码:
#include<iostream>
#include<vector>
#include<unordered_set>
using namespace std;
int operatora[8]={1,2,3,0,0,0,1,2};//做某操作时传教士变化数量
int operatorb[8]={0,0,0,1,2,3,1,1};//做某操作时野人变化数量
struct state{
int al,bl,ar,br;
bool operator==(state s2){
if(this->al==s2.al&&this->ar==s2.ar&&this->bl==s2.bl&&this->br==s2.br) return true;
else return false;
}
};
vector<state> v;
bool Can(state s,int a,int b,int flag){ //判断现在状态是否能做某操作,flag表示左右,1往右,0往左
bool vis=false;
for(int i=0;i<v.size()-1;i++){ //看看之前的轨迹里是否走过该情况,若有,则不走
if(v[i]==s) vis=true;
}
if(vis) return false;
//看看变化后的量是否正常,能不能做该操作
if(flag==1){
if(s.al>=a&&s.bl>=b&&((s.ar+a)>=(s.br+b)||(s.ar+a)==0||(s.br+b)==0)&&((s.al-a)>=(s.bl-b)||(s.al-a)==0||(s.bl-b)==0)) return true;//&&(a>=b||a==0)不知道是否需要
}
else{
if(s.ar>=a&&s.br>=b&&((s.al+a)>=(s.bl+b)||(s.al+a)==0||(s.bl+b)==0)&&((s.ar-a)>=(s.br-b)||(s.ar-a)==0||(s.br-b)==0)) return true;//&&(a>=b||a==0)不知道是否需要
}
return false;
}
int m,n;
void DFS(state s,int flag){
flag==0?flag=1:flag=0;
if(s.ar==m&&s.br==n){ //找到了一种方法
for(int i=0;i<v.size();i++) printf("(%d,%d,%d,%d)%s",v[i].al,v[i].bl,v[i].ar,v[i].br,i==v.size()-1?"\n":"->");
//打印出一条路径
return;
}
for(int i=0;i<8;i++){
if(Can(s,operatora[i],operatorb[i],flag)){
state now;
if(flag==1){
now.al=s.al-operatora[i];
now.ar=s.ar+operatora[i];
now.bl=s.bl-operatorb[i];
now.br=s.br+operatorb[i];
}
else{
now.ar=s.ar-operatora[i];
now.al=s.al+operatora[i];
now.br=s.br-operatorb[i];
now.bl=s.bl+operatorb[i];
}
v.push_back(now);//存下下一个状态
DFS(now,flag); //遍历下一个状态
v.pop_back(); //pop掉该状态
}
}
}
int main(){
cin>>m>>n;
state s;
s.al=m;s.bl=n;s.ar=0;s.br=0;
v.push_back(s);
DFS(s,0);
}
在这次编码中有出现了很多需要注意的点:
- 判断该状态是否能做某操作时要考虑很多条件,比如说是否还大于0,传教士的数目是否大于等于狼人的数目等等
- 刚开始代表向左向右的flag一直没起到作用,后来发现不应该在每一个DFS里都要变,而是要根据父节点的flag来变。