DFS实现传教士野人渡河问题

请将下面这个例子设计成一个状态搜索系统,(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来变。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值