C实现传教士与野人过河问题实验报告
传教士和野人渡河问题
传教士和野人渡河问题刘宪国050422023野人过河问题描述如下:有三个传教士和三个野人过河,只有一条能装下两个人的船,在河的任何一方或者船上,如果野人的人数大于传教士的人数,那么传教士就会有危险.一、算法分析先来看看问题的初始状态和目标状态,假设分为甲岸和乙岸:初始状态:甲岸,3野人,3传教士;乙岸,0野人,0传教士;船停在甲岸,船上有0个人;目标状态:甲岸,0野人,0传教士;乙岸,3野人,3传教士;船停在乙岸,船上有0个人;整个问题就抽象成了怎样从初始状态经中间的一系列状态达到目标状态。
问题状态的改变是通过划船渡河来引发的,所以合理的渡河操作就成了通常所说的算符,根据题目要求,可以得出以下5个算符(按照渡船方向的不同,也可以理解为10个算符):渡1野人、渡1传教士、渡1野人1传教士、渡2野人、渡2传教士。
算符知道以后,剩下的核心问题就是搜索方法了,本文采用深度优先搜索,通过一个FindNext(…)函数找出下一步可以进行的渡河操作中的最优操作,如果没有找到则返回其父节点,看看是否有其它兄弟节点可以扩展,然后用Process(…)函数递规调用FindNext(…),一级一级的向后扩展。
搜索中采用的一些规则如下:1、渡船优先规则:甲岸一次运走的人越多越好(即甲岸运多人优先),同时野人优先运走;乙岸一次运走的人越少越好(即乙岸运少人优先),同时传教士优先运走;2、不能重复上次渡船操作(通过链表中前一操作比较),避免进入死循环;3、任何时候河两边的野人和传教士数均分别大于等于0且小于等于3;4、由于只是找出最优解,所以当找到某一算符(当前最优先的)满足操作条件后,不再搜索其兄弟节点,而是直接载入链表。
5、若扩展某节点a的时候,没有找到合适的子节点,则从链表中返回节点a的父节点b,从上次已经选择了的算符之后的算符中找最优先的算符继续扩展b。
二、基本数据结构定义如下几个数据结构:typedef struct _riverside{ // 岸边状态类型int wildMan; // 野人数int churchMan; // 传教士数}RIVERSIDE;typedef struct _boat{ // 船的状态类型int wildMan; // 野人数int churchMan; // 传教士数}BOAT;typedef struct _question{ // 整个问题状态RIVERSIDE riverSide1; // 甲岸RIVERSIDE riverSide2; // 乙岸int side; // 船的位置, 甲岸为-1, 乙岸为1 BOAT boat; // 船的状态_question* pPrev; // 指向前一渡船操作_question* pNext; // 指向后一渡船操作}QUESTION;用QUESTION来声明一个最基本的链表。
修道士和野人问题
修道⼠和野⼈问题 休闲时刻看看神经⽹络⽅⾯的书,发现了修道⼠和野⼈的问题,不禁勾引起我写算法的欲望,曾经的三只⼤⽼虎三只⼩⽼虎过河问题、⼈狼⽺⽩菜过河问题、汉诺塔、哈夫曼等等各种算法瞬间在脑海中约隐约现,修道⼠和野⼈问题我以前好像没有解开,中午吃饭的时候在脑海中重新构造思路,下午耗了点时间把它⼲掉。
(算法不在代码⾥,⽽在思想中;所以尽量不要看我的代码,⽽要仔细分析我写的思路) 题⽬: 设有3个修道⼠和3个野⼈来到河边,打算⽤⼀条船从河的左岸渡到河的右岸。
但该船每次只能装载两个⼈,在任何岸边野⼈的数⽬都不得超过修道⼠的⼈数,否则修道⼠就会被野⼈吃掉。
假设野⼈服从任何⼀种过河安排,请问如何规划过河计划才能把所有⼈都安全地渡过河去。
⾸先考虑总共有(3+1)*(3+1)= 16 种不同的状态(因为左岸可以有0,1,2,3个传教⼠,左岸可以有0,1,2,3个野⼈),所以可以考虑使⽤穷举法。
使⽤如下C#程序语⾔:int MaxNum = 3;for (int monk = MaxNum; monk >= 0; monk--){for (int savage = MaxNum; savage >= 0; savage--){Console.Write("{{" + monk + "," + savage + "},{" + (MaxNum - monk) + "," + (MaxNum - savage) + "}} ");}Console.Write("\n");}⽣成16种状态图↓↓↓↓↓↓↓↓↓↓↓状态图含义:{a,b}:a,左岸修道⼠数量;b,左岸野⼈数量。
--------仅考虑左岸传教⼠和野蛮⼈数量(所有状态图)------------------------{3,3} {3,2} {3,1} {3,0}{2,3} {2,2} {2,1} {2,0}{1,3} {1,2} {1,1} {1,0}{0,3} {0,2} {0,1} {0,0}其中{3,3}是起始状态图;{0,0}是终⽌状态图。
传教士(牧师)与野人问题-模拟人工智能实验_CSDN博客_传教士与野人问题
传教士(牧师)与野人问题-模拟人工智能实验_结缘缘的博客-CSDN博客_传教士与野人问题题目有n个牧师和n个野人准备渡河但只有一条能容纳c个人的小船为了防止野人侵犯牧师要求无论在何处牧师的人数不得少于野人的人数(除非牧师人数为0) 且假定野人与牧师都会划船试设计一个算法确定他们能否渡过河去若能则给出小船来回次数最少的最佳方案。
实验步骤输入牧师人数(即野人人数) n 小船一次最多载人量c。
输出若问题无解则显示Failed 否则显示Successed输出所有可行方案并标注哪一组是最佳方案。
用三元组(X1, X2, X3)表示渡河过程中的状态。
并用箭头连接相邻状态以表示迁移过程初始状态- 中间状态- 目标状态。
例当输入n 2 c 2时输出221- 200- 211- 010- 021- 000 其中X1表示起始岸上的牧师人数X2表示起始岸上的野人人数X3表示小船现在位置(1表示起始岸0表示目的岸)。
要求写出算法的设计思想和源程序并有用户界面实现人机交互控制台或者窗口都可以进行输入和输出结果如Please input n: 2 Please input c: 2 Optimal Procedure: 221- 200- 211- 010- 021- 000Successed or Failed?: Successed实现代码#include stdio.h #include iostream #include stdlib.h using namespace std;struct State { int Lsavage; int Lgodfather; int Rsavage; int Rgodfather; int boat; //boat at left 0 ; boat at right struct State *States new State[150];struct routesave { int savage; int godfather;struct routesave* routesaves new routesave[150];int godfather, savage, boatnum;void init(State m) { cout 请输入野人和牧师的人数n 以及船的最大载量c endl; int n, c; cin n c; m.Rgodfather n; m.Rsavage n; godfather n, savage n; boatnum c; m.Lgodfather m.Lsavage 0; m.boat 1;void boaloading(int i, int s, int g) { //s个野人和g个传教士if (States[i].boat 0) { routesaves[i].savage s*-1; //左边到右边是负数个野人routesaves[i].godfather g * -1; //左边到右边负数个传教士States[i 1].LsavageStates[i].Lsavage - s; States[i 1].Lgodfather States[i].Lgodfather - g; States[i 1].Rsavage States[i].Rsavage s; States[i 1].Rgodfather States[i].Rgodfather g; States[i 1].boat 1; else{ routesaves[i].savage s; //右边到左边是正数个野人routesaves[i].godfather g; //右边到左边正数个传教士States[i 1].Rsavage States[i].Rsavage-s; States[i 1].RgodfatherStates[i].Rgodfather - g; States[i 1].Lsavage States[i].Lsavage s; States[i 1].Lgodfather States[i].Lgodfather g; States[i 1].boat0;bool checkState(State m) { if (m.Rgodfather 0 m.Rgodfather m.Rsavage) return false; if (m.Lgodfather 0 m.Lgodfatherm.Lsavage) return false; else return true;void showSolution(int i) { cout 问题解决解决路径为endl; for (int c 0; c i; c ) { if (routesaves[c].savage 0) cout 第c 1 步routesaves[c].savage 个野人和routesaves[c].godfather 个传教士乘船去左边endl; else cout 第c 1 步routesaves[c].savage * -1 个野人和routesaves[c].godfather * -1 个传教士乘船去有右边endl; void nextstep(int i) { int c; if (i 150) cout 试探路径过大无法计算; exit(0); for (c 0; c i; c ) /*if the current state is same to previous,retrospect*/ if (States[c].Lsavage States[i].Lsavage States[c].Lgodfather States[i].Lgodfather States[c].Rsavage States[i].Rsavage States[c].Rgodfather States[i].Rgodfather States[c].boat States[i].boat) goto a; if (States[i].Rsavage 0 States[i].Rgodfather 0 States[i].boat 0) { showSolution(i); exit(0); if (States[i].boat 1) { //船在右边for (int s 1; s boatnum s States[i].Rsavage; s ) {//g 0 int g 0; boaloading(i, s, g); if (checkState(States[i 1])) { nextstep(i 1); for (int g 1; g boatnum g States[i].Rgodfather; g ) { //g! 0 for (int s 0; s boatnum - g s States[i].Rsavage s g; s ) { boaloading(i, s, g); if(checkState(States[i 1])) { nextstep(i 1); if (States[i].boat 0) { //船在左边for (int s 1; s boatnum s States[i].Lsavage; s ) {//g 0int g 0; boaloading(i, s, g); if (checkState(States[i 1])) { nextstep(i 1); for (int g 1; g boatnum g States[i].Lgodfather; g ) { //g! 0 for (int s 0; s boatnum - g s States[i].Lsavage s g; s ) { boaloading(i, s, g); if (checkState(States[i 1])) { nextstep(i 1);a:return;void main() { init(States[0]); nextstep(0);实验结果展示。
人工智能的野人与传教士
一、目的与要求目的:使学生加深对图搜索技术的理解,初步掌握图搜索基本编程方法,并能运用图搜索技术解决一些应用问题。
要求:1、可使用第3章中的状态图搜索通用程序,这时只需编写规则集程序;也可用PROLOG语言或其他语言另行编程。
2、程序运行时,应能在屏幕上显示程序运行结果。
二、实验内容与题目内容:传教士和野人问题。
有三个传教士和三个野人一起来到河边准备渡河,河边有一条空船,且传教士和野人都会划船,但每次最多可供两人乘渡。
河的任何一岸以及船上一旦出现野人人数超过传教士人数,野人就会把传教士吃掉。
为安全地渡河,传教士应如何规划渡河方案?题目:图搜索问题求解——过河问题三、实验步骤与源程序源程序:#include <stdlib.h>class CRiver{public:enum{LEFT_BANK = 1, RIGHT_BANK = 0};enum{CHUCHMEN = 0, GOTH = 1};friend ostream & operator << (ostream & Out, CRiver river);bool L01();bool L10();bool L11();bool L02();bool L20();bool R01();bool R10();bool R11();bool R02();bool R20();bool Compare(const CRiver & riverDet);CRiver(const CRiver & pSrc);CRiver();virtual ~CRiver();int m_nShipPos;int m_nChuchmenAndGoth[2][2];CRiver * m_pParent;CRiver * m_pNextStep;protected:private:};CRiver::CRiver(){int i = 0;m_nShipPos = LEFT_BANK;m_pParent = NULL;m_pNextStep = NULL;for (i=0; i<2; i++){m_nChuchmenAndGoth[LEFT_BANK][i] = 3;m_nChuchmenAndGoth[RIGHT_BANK][i] = 0;}}CRiver::~CRiver(){}ostream & operator << (ostream & Out, CRiver river){Out<<"(m, c, b): ";Out<<"(";Out<<river.m_nChuchmenAndGoth[CRiver::LEFT_BANK][CRiver::CHUCHMEN]<<", ";Out<<river.m_nChuchmenAndGoth[CRiver::LEFT_BANK][CRiver::GOTH]<<", ";Out<<river.m_nShipPos<<")";return Out;}bool CRiver::Compare(const CRiver & riverDet){int i = 0;int j = 0;if (m_nShipPos != riverDet.m_nShipPos){return false;}for (i=0; i<2; i++){for (j=0; j<2; j++){if (m_nChuchmenAndGoth[i][j] != riverDet.m_nChuchmenAndGoth[i][j]){return false;}}}return true;}CRiver::CRiver(const CRiver & pSrc){int i = 0;int j = 0;m_pParent = pSrc.m_pParent;m_nShipPos = pSrc.m_nShipPos;for (i=0; i<2; i++){for (j=0; j<2; j++){m_nChuchmenAndGoth[i][j] = pSrc.m_nChuchmenAndGoth[i][j];}}}bool CRiver::L01(){CRiver oldRiver = *this;if (LEFT_BANK == m_nShipPos&& m_nChuchmenAndGoth[LEFT_BANK][GOTH] >= 1&& 0 == (m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN] % 3)){m_nShipPos = RIGHT_BANK;m_nChuchmenAndGoth[LEFT_BANK][GOTH]--;m_nChuchmenAndGoth[RIGHT_BANK][GOTH]++;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::L10(){CRiver oldRiver = *this;if (LEFT_BANK == m_nShipPos&& ((2==m_nChuchmenAndGoth[LEFT_BANK][GOTH] &&3==m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN])|| (1==m_nChuchmenAndGoth[LEFT_BANK][GOTH] &&1==m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN]))){m_nShipPos = RIGHT_BANK;m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN]--;m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN]++;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::L11(){CRiver oldRiver = *this;if (LEFT_BANK == m_nShipPos&& m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN] >= 1&& (m_nChuchmenAndGoth[LEFT_BANK][GOTH] ==m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN])){m_nShipPos = RIGHT_BANK;m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN]--;m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN]++;m_nChuchmenAndGoth[LEFT_BANK][GOTH]--;m_nChuchmenAndGoth[RIGHT_BANK][GOTH]++;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::L02(){CRiver oldRiver = *this;if (LEFT_BANK == m_nShipPos&& m_nChuchmenAndGoth[LEFT_BANK][GOTH] >= 2&& 0 == (m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN] % 3)){m_nShipPos = RIGHT_BANK;m_nChuchmenAndGoth[LEFT_BANK][GOTH] = m_nChuchmenAndGoth[LEFT_BANK][GOTH] - 2;m_nChuchmenAndGoth[RIGHT_BANK][GOTH]= m_nChuchmenAndGoth[RIGHT_BANK][GOTH] + 2;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::L20(){CRiver oldRiver = *this;if (LEFT_BANK == m_nShipPos&& ((2==m_nChuchmenAndGoth[LEFT_BANK][GOTH] &&2==m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN])|| (1==m_nChuchmenAndGoth[LEFT_BANK][GOTH] &&3==m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN]))){m_nShipPos = RIGHT_BANK;m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN] =m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN] - 2;m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN]=m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN] + 2;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::R01(){CRiver oldRiver = *this;if (RIGHT_BANK == m_nShipPos&& m_nChuchmenAndGoth[RIGHT_BANK][GOTH] >= 1&& 0 == (m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN] % 3)) {m_nShipPos = LEFT_BANK;m_nChuchmenAndGoth[RIGHT_BANK][GOTH]--;m_nChuchmenAndGoth[LEFT_BANK][GOTH]++;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::R10(){CRiver oldRiver = *this;if (RIGHT_BANK == m_nShipPos&& ((2==m_nChuchmenAndGoth[RIGHT_BANK][GOTH] &&3==m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN])|| (1==m_nChuchmenAndGoth[RIGHT_BANK][GOTH] &&1==m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN]))){m_nShipPos = LEFT_BANK;m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN]--;m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN]++;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::R11(){CRiver oldRiver = *this;if (RIGHT_BANK == m_nShipPos&& m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN] >= 1&& (m_nChuchmenAndGoth[RIGHT_BANK][GOTH] ==m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN])){m_nShipPos = LEFT_BANK;m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN]--;m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN]++;m_nChuchmenAndGoth[RIGHT_BANK][GOTH]--;m_nChuchmenAndGoth[LEFT_BANK][GOTH]++;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::R02(){CRiver oldRiver = *this;if (RIGHT_BANK == m_nShipPos&& m_nChuchmenAndGoth[RIGHT_BANK][GOTH] >= 2&& 0 == (m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN] % 3)){m_nShipPos = LEFT_BANK;m_nChuchmenAndGoth[RIGHT_BANK][GOTH] = m_nChuchmenAndGoth[RIGHT_BANK][GOTH] - 2;m_nChuchmenAndGoth[LEFT_BANK][GOTH]= m_nChuchmenAndGoth[LEFT_BANK][GOTH] + 2;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}bool CRiver::R20(){CRiver oldRiver = *this;if (RIGHT_BANK == m_nShipPos&& ((2==m_nChuchmenAndGoth[RIGHT_BANK][GOTH] &&2==m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN])|| (1==m_nChuchmenAndGoth[RIGHT_BANK][GOTH] &&3==m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN]))){m_nShipPos = LEFT_BANK;m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN] =m_nChuchmenAndGoth[RIGHT_BANK][CHUCHMEN] - 2;m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN]=m_nChuchmenAndGoth[LEFT_BANK][CHUCHMEN] + 2;if (NULL != m_pParent){if (Compare(*m_pParent)&& m_nShipPos == m_pParent->m_nShipPos){*this = oldRiver;return false;}}return true;}return false;}void main(){bool bFinded = false;CRiver * pRiverOpenQueue[1024];int nHeadOpen = 0;int nRearOpen = 0;CRiver * pRiverClosedQueue[1024];int nHeadClosed = 0;int nRearClosed = 0;CRiver * pRiverS[1024];int nNo = -1;CRiver * pRiverCur = NULL;int i = 0;CRiver riverTemp;CRiver riverSg;// 初始化目标结点riverSg.m_nShipPos = CRiver::RIGHT_BANK;riverSg.m_pParent = NULL;for (i=0; i<2; i++){riverSg.m_nChuchmenAndGoth[CRiver::LEFT_BANK][i] = 0;riverSg.m_nChuchmenAndGoth[CRiver::RIGHT_BANK][i] = 3;}// 初始化pRiverSfor (i=0; i<1024; i++){pRiverS[i] = NULL;pRiverOpenQueue[i] = NULL;pRiverClosedQueue[i] = NULL;}// 把S0放入OPEN表pRiverS[++nNo] = new CRiver;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}while (nRearOpen > nHeadOpen) // OPEN表不为空{// 把第一个结点n,从OPEN表中移出,并把它放入CLOSED表中pRiverCur = pRiverOpenQueue[nHeadOpen++];if (nRearClosed < 1024){pRiverClosedQueue[nRearClosed++] = pRiverCur;}// 扩展n,把它的后继结点放入OPEN表的末端,提供回到n的指针riverTemp = *pRiverCur;if (CRiver::LEFT_BANK == riverTemp.m_nShipPos){if (riverTemp.L01()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{// 成功bFinded = true;break;}}if (riverTemp.L10()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{// 成功bFinded = true;break;}}if (riverTemp.L11()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{bFinded = true;break;}}if (riverTemp.L02()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{// 成功bFinded = true;break;}}if (riverTemp.L20()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{bFinded = true;break;}}}else{if (riverTemp.R01()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{// 成功bFinded = true;break;}}if (riverTemp.R10()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{bFinded = true;break;}}if (riverTemp.R11()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{// 成功bFinded = true;break;}}if (riverTemp.R02()){// 成功生成一个后继结点pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{bFinded = true;break;}}if (riverTemp.R20()){pRiverS[++nNo] = new CRiver;*pRiverS[nNo] = riverTemp;pRiverS[nNo]->m_pParent = pRiverCur;if (NULL == pRiverS[nNo]){cout<<"内存不足!"<<endl;exit(1);}if (nRearOpen < 1024){pRiverOpenQueue[nRearOpen++] = pRiverS[nNo];}riverTemp = *pRiverCur;if (pRiverS[nNo]->Compare(riverSg)) // 有后继结点为目标结点{bFinded = true;break;}}}}if (bFinded){CRiver * riverP = NULL;pRiverS[nNo]->m_pNextStep = NULL;riverP = pRiverS[nNo];while (riverP->m_pParent != NULL){(riverP->m_pParent)->m_pNextStep = riverP;riverP = riverP->m_pParent;}cout<<"D052 翁克松:"<<endl;while (riverP != NULL){cout<<riverP->m_nChuchmenAndGoth[CRiver::LEFT_BANK][CRiver::CHUCHMEN]<<","; cout<<nShipPos<<")"<<endl;cout<<*riverP<<endl;riverP = riverP->m_pNextStep;}}while (nNo > 0){delete pRiverS[nNo];pRiverS[nNo] = NULL;nNo--;}}四、测试数据和实验结果五、结果分析和实验体会通过本次实验,加深了对图搜索技术的理解,初步掌握了图搜索基本编程方法,并能运用图搜索技术解决一些基本的应用问题。
MC牧师过河问题
MC牧师过河问题⼈⼯智能上机实验报告学号:姓名:所在系:信息学院班级:实验名称:实验⽇期2016年12⽉3⽇实验指导教师实验机房A401------------------------------------------------------------------------------------------------------ 1.实验⽬的:(1)在掌握状态空间搜索策略的基础上,理解知识表⽰的⽅法。
(2)能够应⽤知识表⽰⽅法,解决实际问题2. 实验内容:(1)M-C问题描述有n个牧师和n个野⼈准备渡河,但只有⼀条能容纳c个⼈的⼩船,为了防⽌野⼈侵犯牧师,要求⽆论在何处,牧师的⼈数不得少于野⼈的⼈数(除⾮牧师⼈数为0),且假定野⼈与牧师都会划船,试设计⼀个算法,确定他们能否渡过河去,若能,则给出⼩船来回次数最少的最佳⽅案。
3.算法设计(编程思路或流程图或源代码)#include#include#include#define maxloop 100 /* 最⼤层数,对于不同的扩展⽅法⾃动调整取值*/#define pristnum 3 /*初始化时设定有3个野⼈3个牧师,实际可以改动*/#define slavenum 3struct SPQ{ int sr,pr; /* 船运⾏⼀个来回后河右岸的野⼈、牧师的⼈数*/int sl,pl; /* 船运⾏⼀个来回后河左岸的野⼈、牧师的⼈数*/int ssr,spr; /* 回来(由左向右时)船上的⼈数*/int sst,spt; /* 去时(由右向左时)船上的⼈数*/int loop; /* 本结点所在的层数*/struct SPQ *upnode ,*nextnode;/* 本结点的⽗结点和同层的下⼀个结点的地址*/}spq;int loopnum;/* 记录总的扩展次数*/int openednum;/* 记录已扩展节点个数*/int unopenednum;/* 记录待扩展节点个数*/int resultnum;struct SPQ *opened;struct SPQ *oend;struct SPQ *unopened;struct SPQ *uend;struct SPQ *result;void initiate();void releasemem();void showresult();void addtoopened(struct SPQ *ntx);int search();void goon();int stretch(struct SPQ* ntx);void recorder();void addtoopened(struct SPQ *ntx) /*扩展节点*/ { unopened = unopened -> nextnode; unopenednum--; if (openednum == 0 )oend = opened = ntx;oend -> nextnode = ntx;oend = ntx;openednum++;}void recorder(){int i , loop;struct SPQ *newnode;struct SPQ *ntx;loop = oend -> loop;ntx = oend;resultnum = 0;for( i = 0 ; i <= loop ; i++ ){newnode = (struct SPQ*) 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; result = newnode;resultnum++;}}void releasemem(){int i;struct SPQ* nodefree;for ( i = 1 ; i < openednum ; i++ ) {nodefree = opened;opened = opened -> nextnode;free(nodefree);}for ( i = 0 ; i < unopenednum ; i++ ) {nodefree = unopened;unopened = unopened -> nextnode; free(nodefree);}}void showresult() /*显⽰*/{int i;int fsr , fpr ; /* 在右岸上的⼈数*/ int fsl , fpl ; /* 在左岸上的⼈数*/ struct SPQ* nodefree;printf("%d个牧师",result -> pr); printf("%d个野⼈",result -> sr); printf("%d个牧师",result -> pl); printf("%d个野⼈",result -> sl); for ( i = 1 ; i < resultnum ; i++ ) {nodefree = result;result = result -> nextnode;free(nodefree);printf("\n\n\t左岸⼈数船上⼈数及⽅向右岸⼈数\n");printf("第%d轮\n",i);fpl = result -> pl - result -> spt + result -> spr;fpr = result -> pr - result -> spr;fsl = result -> sl - result -> sst + result -> ssr;fsr = result -> sr - result -> ssr;printf("牧师%8d%8d\t<-\t%8d\n",fpl,result -> spt,fpr);printf("野⼈%8d%8d\t<-\t%8d\n",fsl,result -> sst,fsr);printf("牧师%8d%8d\t->\t%8d\n",result -> pl,result -> spr,result -> pr - result -> spr); printf("野⼈%8d%8d\t->\t%8d\n",result -> sl,result -> ssr,result -> sr - result -> ssr); }printf("\n全体牧师和野⼈全部到达对岸");free(result);}void goon() /*循环操作选择*/{char choice;for(;;){printf("\n是否继续?(Y/N)\n");scanf ("%s" , &choice);choice=toupper(choice);if(choice=='Y')break;if(choice=='N')exit(0);}}int main(){int flag; /* 标记扩展是否成功*/for( ; ; ){initiate();flag = search ();if(flag == 1){recorder();releasemem();showresult();goon();}else{printf("⽆法找到符合条件的解");releasemem();goon();}}system("pause");return 0;}void initiate(){int x;char choice;uend = unopened = (struct SPQ*)malloc(sizeof(spq));if(uend==NULL){printf("\n内存不够!\n");exit(0);}unopenednum=1;openednum=0;unopened -> upnode = unopened; /* 保存⽗结点的地址以成链表*/ unopened -> nextnode = unopened; unopened -> sr = slavenum;unopened -> pr = pristnum;unopened -> sl = 0;unopened -> pl = 0;unopened -> sst = 0;unopened -> spt = 0;unopened -> ssr = 0;unopened -> spr = 0;unopened -> loop = 0;printf("\n请输⼊牧师⼈数");for(;;){scanf("%d",&x);if(x>0){unopened -> pr = x;break;}else printf("\n输⼊值应⼤于0!\n请重新输⼊");}printf("\n请输⼊野⼈⼈数");for(;;){scanf("%d",&x);if(x>0){unopened -> sr = x;break;}else printf("\n输⼊值应⼤于0!\n请重新输⼊");}}int search(){int flag;struct SPQ *ntx; /* 提供将要扩展的结点的指针*/for( ;; ){ntx = unopened; /* 从待扩展链表中提取最前⾯的⼀个*/if(ntx->loop == maxloop)return 0;addtoopened(ntx); /* 将ntx加⼊已扩展链表,并将这个节点从待扩展链表中去掉*/ flag = stretch(ntx); /* 对ntx进⾏扩展,返回-1,0,1 */if(flag == 1)return 1;}}int stretch(struct SPQ *ntx){int fsr , fpr ; /* 在右岸上的⼈数*/int fsl , fpl ; /* 在左岸上的⼈数*/int sst , spt ; /* 出发时在船上的⼈数*/int ssr , spr ; /* 返回时船上的⼈数*/struct SPQ *newnode;for (sst = 0 ; sst <= 2 ; sst++) /* 讨论不同的可能性并判断是否符合条件*/ { fsr = ntx -> sr;fpr = ntx -> pr;fsl = ntx -> sl;fpl = ntx -> pl;if ((sst <= fsr) && (( 2 - sst) <= fpr))/* 满⾜⼈数限制*/{spt = 2 - sst;fsr = fsr - sst;fpr = fpr - spt;if((fpr == 0) && (fsr == 0))/* 搜索成功*/{newnode = (struct SPQ*) malloc (sizeof(spq));if(newnode==NULL){printf("\n内存不够!\n");exit(0);}newnode -> upnode = ntx; /* 保存⽗结点的地址以成链表*/newnode -> nextnode = NULL;newnode -> sr = 0;newnode -> pr = 0;newnode -> sl = opened -> sr;newnode -> pl = opened -> pr;newnode -> spr = 0;newnode -> loop = ntx -> loop + 1;oend -> nextnode = newnode;oend = newnode;openednum++;return 1;}else if ((fpr - fsr) * fpr >= 0) /* 判断是否满⾜传教⼠⼈数必须⼤于或等于野⼈⼈数*/ {fsl = fsl + sst;fpl = fpl + spt;for (ssr = 0 ; ssr <= 1 ; ssr++) /* 返回*/{int ffsl , ffpl;if ((ssr <= fsl) && ((1 - ssr) <= fpl)){spr = 1 - ssr;ffsl = fsl - ssr;ffpl = fpl - spr;if ((ffpl - ffsl) * ffpl >= 0){ /* 若符合条件则分配内存并付值*/int ffsr , ffpr;ffsr = fsr + ssr;ffpr = fpr + spr;newnode = (struct SPQ*) malloc (sizeof(spq));if(newnode==NULL){printf("\n内存不够!\n");exit(0);}newnode -> upnode = ntx; /* 保存⽗结点的地址以成链表*/ newnode -> sr = ffsr; newnode -> pr = ffpr;newnode -> spt = spt;newnode -> ssr = ssr;newnode -> spr = spr;newnode -> loop = ntx -> loop + 1;uend -> nextnode = newnode;uend = newnode;unopenednum++;}}}}}}return 0;}4.程序调试(实验数据记录——根据程序要求输⼊⼏组不同数据,记录程序运⾏结果,并分析结果,分析程序运⾏中出现的主要错误。
传教士和野人渡河
传教士和野人渡河问题作品报告有5个传教士和5个野人过河,只有一条能装下3个人的船,在河的任何一方或者船上,如果野人的人数大于传教士的人数,那么传教士就会有危险。
请设计合适的摆渡方案,并使得所需的摆渡次数最少。
源代码:#include "stdio.h"#include "string.h"#define STEP_MAX 20 //来回过河的最大次数#define NO 5 //野人和传教士各有的人数#define HEAVY 3 //船的最大载重量typedef struct{int wild; //右岸上的野人数int man; //右岸上的传教士数int boat_state; //0表示在左岸,1表示在右岸}state;typedef struct{int wild; //船上的野人数int man; //船上的传教士数int boat_run; //0表示去左岸,1表示去右岸}boat;state now[STEP_MAX]={0}; //保存过河过程中对岸的状态boat path[STEP_MAX]={0}; //保存过河的路径int suit_NO=STEP_MAX; //最合适的过河次数,初始值为一足够的数boat suit_path[STEP_MAX]; //最合适的过河方式//是否全部过河bool All(state c){//右岸的最终状态const state cs={NO,NO,1};if(memcmp(&cs,&c,sizeof(state))==0){return true;}return false;}//传教士是否有危险bool Danger(state ca){if ((ca.wild>ca.man&&ca.man>0)||(NO-ca.wild>NO-ca.man&&NO-ca.man>0)) {return true;}elsereturn false;}//判断该状态是否与前面的一样bool Same(state cs,int n){for (int i=0;i<n;i++){if (memcmp(&cs,&now[i],sizeof(state))==0){return true;}}return false;}//将最短路径保存到suit_path中void Min(int n,boat path[]){if (n<suit_NO){suit_NO=n;memcpy(&suit_path[0],&path[0],n*sizeof(boat));}}//查找过河方案void Cross(int n){int i,j;if (All(now[n])){Min(n,path);return;}if (Danger(now[n])){return;}if (Same(now[n],n)){return;}if(now[n].boat_state==0)//船在左岸时{for(i=0;i<=HEAVY && i<=NO-now[n].wild;i++){for(j=0;j<=HEAVY-i && j<=NO-now[n].man;j++){if (i==0 && j==0){continue;}path[n].wild=i;path[n].man=j;path[n].boat_run=1;memcpy(&now[n+1],&now[n],sizeof(state));now[n+1].wild+=i;now[n+1].man+=j;now[n+1].boat_state=1;Cross(n+1);}}}else//船在右岸时{for(i=0;i<=HEAVY && i<=now[n].wild;i++){for(j=0;j<=HEAVY-i && j<=now[n].man;j++){if (i==0 && j==0){continue;}path[n].wild=i;path[n].man=j;path[n].boat_run=0;memcpy(&now[n+1],&now[n],sizeof(state));now[n+1].wild-=i;now[n+1].man-=j;now[n+1].boat_state=0;Cross(n+1);}}}}void main(){Cross(0);for(int i=0;i<suit_NO;i++){if(path[i].boat_run==0)printf("第%d次过河,从右岸到左岸,船上野人数为%d,传教士数为%d.\n",i+1,suit_path[i].wild,suit_path[i].man);elseprintf("第%d次过河,从左岸到右岸,船上野人数为%d,传教士数为%d.\n",i+1,suit_path[i].wild,suit_path[i].man);}}使用方法:上述程序主要采用了递归调用和近似于枚举的方法。
传教士和野人过河
实验报告一、实验名称:传教士和野人过河二、实验目的:这是经典的过河方案规划问题,通过本实验的设计与编程实现让学生掌握基于状态空间知识表示方法的一般搜索策略。
三、实验内容:设有3个传教士和3个野人同在河的左岸,他们都要到对岸去;河里只有一条船,他们都会划船,但每次渡船至多只能乘两人;如果在任何一岸上,也认的数量超过传教士,野人就要吃掉传教士,要求设计算法,用船将3个传教士和3个野人安全的从左岸移到右岸。
四、实验设计(一)所用的语言:c++语言(二)数据结构节点状态用列表(m,c,b)表示,其中m表示传教士在左岸的人数; c表示野人在左岸的人数;b表示船是否在左岸,当b=1时,表示船在左岸,当b=0时,表式船在右岸。
初始状态:(3,3,1)目标状态: (0,0,0)操作算子:船上人数组合(m,c)共5种(1,0),(1,1),(2,0),(0,1),(0,2)因此算法有10种1)从右岸向左岸过1个传教士,0个野人2)从右岸向左岸过1个传教士,1个野人3)从右岸向左岸过2个传教士,0个野人4)从右岸向左岸过0个传教士,1个野人5)从右岸向左岸过0个传教士,2个野人6)从左岸向右岸过1个传教士,0个野人7)从左岸向右岸过1个传教士,1个野人8)从左岸向右岸过2个传教士,0个野人9)从左岸向右岸过0个传教士,1个野人10)从左岸向右岸过0个传教士,2个野人状态节点: typedef struct st{int m;//传教士int c;//野人int b;//船左}state;//状态将有效的节点存储在树中Tree 中的节点typedef struct hnode{state s;struct hnode *left;struct hnode *right;}node;Open表,closed表用队列存储//定义队列中的节点typedef struct Queuenode{node * np;struct Queuenode* next;}Qnode;//队列中节点//定义队列typedef struct Queue{Qnode *front;Qnode *rear;}queue;(三)算法流程1.用起始节点(3,3,1) 初始化tree,初始化open表,closed表。
人工智能实验报告
实验一:知识表示方法一、实验目的状态空间表示法是人工智能领域最基本的知识表示方法之一,也是进一步学习状态空间搜索策略的基础,本实验通过牧师与野人渡河的问题,强化学生对知识表示的了解和应用,为人工智能后续环节的课程奠定基础。
二、问题描述有n个牧师和n个野人准备渡河,但只有一条能容纳c个人的小船,为了防止野人侵犯牧师,要求无论在何处,牧师的人数不得少于野人的人数(除非牧师人数为0),且假定野人与牧师都会划船,试设计一个算法,确定他们能否渡过河去,若能,则给出小船来回次数最少的最佳方案。
三、基本要求输入:牧师人数(即野人人数):n;小船一次最多载人量:c。
输出:若问题无解,则显示Failed,否则,显示Successed输出一组最佳方案。
用三元组(X1, X2, X3)表示渡河过程中的状态。
并用箭头连接相邻状态以表示迁移过程:初始状态->中间状态->目标状态。
例:当输入n=2,c=2时,输出:221->110->211->010->021->000其中:X1表示起始岸上的牧师人数;X2表示起始岸上的野人人数;X3表示小船现在位置(1表示起始岸,0表示目的岸)。
要求:写出算法的设计思想和源程序,并以图形用户界面实现人机交互,进行输入和输出结果,如:Please input n: 2 Please input c: 2Successed or Failed?: SuccessedOptimal Procedure: 221->110->211->010->021->000四、实验组织运行要求本实验采用集中授课形式,每个同学独立完成上述实验要求。
五、实验条件每人一台计算机独立完成实验。
六、实验代码Main.cpp#include<iostream>#include"RiverCrossing.h"using namespace std;//主函数void main(){RiverCrossing::ShowInfo();int n, c;cout<<"Please input n: ";cin>>n;cout<<"Please input c: ";cin>>c;RiverCrossing riverCrossing(n, c);riverCrossing.solve();system("pause");}RiverCrossing.h #pragma once#include<list>//船class Boat{public:static int c;int pastor;//牧师int savage;//野人Boat(int pastor, int savage);};//河岸状态class State{public:static int n;int iPastor;//牧师数量int iSavage;//野人数量int iBoatAtSide;//船所在河岸State *pPrevious;//前一个状态State(int pastor, int savage, int boatAtSide);int getTotalCount();//获得此岸总人数bool check();//检查人数是否符合实际bool isSafe();//检查是否安全State operator + (Boat &boat);State operator - (Boat &boat);bool operator == (State &state);};//过河问题class RiverCrossing{private:std::list<State*> openList, closeList;State endState;bool move(State *nowState, Boat *boat);//进行一次决策State* findInList(std::list<State*> &listToCheck, State &state);//检查某状态节点是否在列表中void print(State *endState);//打印结果public:static void ShowInfo();RiverCrossing(int n, int c);bool solve();//求解问题};RiverCrossing.cpp#include"RiverCrossing.h"#include<iostream>#include<stack>#include<algorithm>using namespace std;//类静态变量定义int State::n = 0;int Boat::c = 0;/*=========================Methods for class "Boat"=========================*/ Boat::Boat(int pastor, int savage){this->pastor = pastor;this->savage = savage;}/*=========================Methods for class "State"=========================*/ //构造函数State::State(int pastor, int savage, int boatAtSide){this->iPastor = pastor;this->iSavage = savage;this->iBoatAtSide = boatAtSide;this->pPrevious = NULL;}//获取此岸总人数int State::getTotalCount(){return iPastor + iSavage;}//检查人数是否在0到n之间bool State::check(){return (iPastor >=0 && iPastor <= n && iSavage >= 0 && iSavage <=n);}//按照规则检查牧师得否安全bool State::isSafe(){//此岸的安全:x1 == 0 || x1 >= x2//彼岸的安全:(n-x1) == 0 || (n-x1) >= (n-x2)//将上述条件联立后得到如下条件return (iPastor == 0 || iPastor == n || iPastor == iSavage);}//重载+符号,表示船开到此岸State State::operator+(Boat &boat){State ret(iPastor + boat.pastor, iSavage + boat.savage, iBoatAtSide + 1);ret.pPrevious = this;return ret;}//重载-符号,表示船从此岸开走State State::operator-(Boat &boat){State ret(iPastor - boat.pastor, iSavage - boat.savage, iBoatAtSide - 1);ret.pPrevious = this;return ret;}//重载==符号,比较两个节点是否是相同的状态bool State::operator==(State &state){return (this->iPastor == state.iPastor && this->iSavage == state.iSavage && this->iBoatAtSide == state.iBoatAtSide);}/*=======================Methods for class "RiverCrossing"=======================*/ //显示信息void RiverCrossing::ShowInfo(){cout<<"************************************************"<<endl;cout<<" 牧师与野人过河问题求解 "<<endl;cout<<" by 1040501211 陈嘉生 "<<endl;cout<<"************************************************"<<endl;}//构造函数RiverCrossing::RiverCrossing(int n, int c):endState(0, 0, 0){State::n = n;Boat::c = c;}//解决问题bool RiverCrossing::solve(){openList.push_back(new State(State::n, State::n, 1));while(!openList.empty()) {//获取一个状态为当前状态State *nowState = openList.front();openList.pop_front();closeList.push_back(nowState);//从当前状态开始决策if (nowState->iBoatAtSide == 1) {//船在此岸//过河的人越多越好,且野人优先int count = nowState->getTotalCount();count = (Boat::c >= count ? count : Boat::c);for (int capticy = count; capticy >= 1; --capticy) {for (int i = 0; i <= capticy; ++i) {Boat boat(i, capticy - i);if (move(nowState, &boat))return true;}}} else if (nowState->iBoatAtSide == 0) {//船在彼岸//把船开回来的人要最少,且牧师优先for (int capticy = 1; capticy <= Boat::c; ++capticy) { for (int i = 0; i <= capticy; ++i) {Boat boat(capticy - i, i);if (move(nowState, &boat))return true;}}}}print(NULL);return false;}//实施一步决策,将得到的新状态添加到列表,返回是否达到目标状态bool RiverCrossing::move(State *nowState, Boat *boat){//获得下一个状态State *destState;if (nowState->iBoatAtSide == 1) {destState = new State(*nowState - *boat);//船离开此岸} else if (nowState->iBoatAtSide == 0) {destState = new State(*nowState + *boat);//船开到此岸}if (destState->check()) {//检查人数if (*destState == endState) {//是否达到目标状态closeList.push_back(destState);print(destState);return true;//找到结果} else if (destState->isSafe()) {//检查是否安全if (!findInList(openList, *destState) && !findInList(closeList,*destState)) {//检查是否在表中//添加没出现过的状态节点到open表openList.push_back(destState);return false;}}}delete destState;return false;}//检查给定状态是否存在于列表中State* RiverCrossing::findInList(list<State*> &listToCheck, State &state){for (list<State*>::iterator ite = listToCheck.begin(); ite != listToCheck.end(); ++ite) {if (**ite == state)return *ite;}return NULL;}//根据达到的目标状态,回溯打印出求解过程void RiverCrossing::print(State *endState){cout<<"================================================"<<endl;if (!endState) {cout<<"Search failed!"<<endl;} else {cout<<"Search successed!"<<endl;cout<<"Optimal Procedure: "<<endl;State *pState = endState;stack<State*> st;//用栈将链表逆序,以便输出while (pState) {st.push(pState);pState = pState->pPrevious;}int count = 0;while (!st.empty()) {pState = st.top();st.pop();cout<<pState->iPastor<<","<<pState->iSavage<<","<<pState->iBoatAtSide;if (st.size() > 0)cout<<" -> ";if (++count % 5 == 0)//每五个步骤换行cout<<endl;}cout<<endl;cout<<"Total move: "<<count - 1<<endl;}cout<<"================================================"<<endl;}七、实验结果实验二:九宫重排一、实验目的A*算法是人工智能领域最重要的启发式搜索算法之一,本实验通过九宫重排问题,强化学生对A*算法的理解与应用,为人工智能后续环节的课程奠定基础。
修道士与野人问题
6.修道士与野人问题这是一个古典问题。
假设有n个修道士和n个野人准备渡河,但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数(除非修道士个数为0)。
如果两种人都会划船,试设计一个算法,确定他们能否渡过河去,若能,则给出一个小船来回次数最少的最佳方案。
要求:(1)用一个三元组(x1,x2,x3)表示渡河过程中各个状态。
其中,x1表示起始岸上修道士个数,x2表示起始岸上野人个数,x3表示小船位置(0——在目的岸,1——在起始岸)。
例如(2,1,1)表示起始岸上有两个修道士,一个野人,小船在起始岸一边。
采用邻接表做为存储结构,将各种状态之间的迁移图保存下来。
(2)采用广度搜索法,得到首先搜索到的边数最少的一条通路。
(3)输出数据若问题有解(能渡过河去),则输出一个最佳方案。
用三元组表示渡河过程中的状态,并用箭头指出这些状态之间的迁移:目的状态←…中间状态←…初始状态。
若问题无解,则给出“渡河失败”的信息。
(4)求出所有的解。
1.需求分析有n个修道士和n个野人准备渡河,但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的人数,否则修道士就会有危险,设计一个算法,确定他们能否渡过河去,若能,则给出一个小船来回次数最少的最佳方案。
用三元组(x1,x2,x3)来表示渡河过程中各个状态,其中,x1表示起始岸上修道士个数,x2表示起始岸上野人个数,x3表示小船位置(0——在目的岸,1——在起始岸)。
若问题有解(能渡过河去),则输出一个最佳方案。
用三元组表示渡河过程中的状态,并用箭头指出这些状态之间的迁移:目的状态←…中间状态←…初始状态,若问题无解,则给出“渡河失败”的信息。
2.设计2.1 设计思想(1)数据结构设计逻辑结构设计: 图型结构存储结构设计: 链式存储结构采用这种数据结构的好处:便于采用广度搜索法,得到首先搜索到的边数最少的一条通路,输出一个最佳方案,采用图的邻接表存储结构搜索效率较高。
有N个传教士和N个野人来到河边渡河
有N个传教士和N个野人来到河边渡河, 河岸有一条船, 每次至多可供k人乘渡。
问传教士为了安全起见, 应如何规划摆渡方案, 使得任何时刻, 河两岸以及船上的野人数目总是不超过传教士的数目(否则不安全, 传教士有可能被野人吃掉)。
即求解传教士和野人从左岸全部摆渡到右岸的过程中, 任何时刻满足M(传教士数)≥C(野人数)和M+C≤k的摆渡方案。
我们此处举例, 只讨论N为3、k为2的乘渡问题, 这样传教士和野人问题的描述就具体为如下:三个传教士与三个野人来到河边, 有一条船可供一人或两人乘渡, 问题是如何用这条船渡河才能使得河的任一岸上野人的数目总不超过传教士的数目(当然, 如果某一岸上只有野人而没有传教士是允许的)?我们用一个三元组(m c b)来表示河岸上的状态, 其中m、c分别代表某一岸上传教士与野人的数目, b=1表示船在这一岸, b=0则表示船不在。
设N=3, k=2, 则给定的问题可用下图表示, 图中L和R表示左岸和右岸, B=1或0分别表示有船或无船。
约束条件是: 两岸上M≥C, 船上M+C≤2。
我们采用产生式系统来解决这一问题。
由于传教士与野人的总数目是一常数, 所以只要表示出河的某一岸上的情况就可以了, 为方便起见, 我们选择传教士与野人开始所在的岸为所要表示的岸, 并称其为左岸, 另一岸称为右岸。
但显然仅用描述左岸的三元组描述就足以表示出整个情况, 因此必须十分重视选择较好的问题表示法。
以后的讨论还可以看到高效率的问题求解过程与控制策略有关, 合适的控制策略可缩小状态空间的搜索范围, 提高求解的效率。
因而问题的初始状态是(3 3 1), 目标状态是(0 0 0)。
(1) 综合数据库: 用三元组表示, 即(ML, CL, BL), 其中0≤ML, CL≤3, BL∈{0, 1}此时问题述简化为(3, 3, 1)® (0, 0, 0)N=3的M-C问题, 状态空间的总状态数为4×4×2=32, 根据约束条件的要求, 可以看出只有20个合法状态。
数据结构 第7章 修道士 实验报告
数据结构实验报告第七章实验名称:修道士野人问题实验类型:综合性实验班级:学号:姓名:实验日期:2014年5月21日1.问题描述河的左岸有N个野人和N个修道士以及一条小船,修道士们想用这条小船把所有的人都运到河的右岸,但又受到以下限制:●修道士和野人都会划船,但船一次只能载C人。
●在任何岸边,为了防止野人侵犯修道士,野人数不能超过修道士数,否则修道士将会被野人吃掉。
假定野人愿意服从任何一种过河的安排,本设计的主要任务是规划出一种确保修道士安全的过河方案。
2.数据结构设计用一个三元组(num1,num2,an)表示渡河过程中的各个状态。
num1表示左岸上修道士的个数,num2表示左岸上野人的个数,an表示小船位置(0-在右岸上,1-在左岸上)。
定义一个结构体,用于存放各个时刻的状态:typedef struct{int num1;//修道士人数int num2;//野蛮人人数int an;//表示两岸,0在右岸,1在左岸}DataType;用邻接表存储结构实现图的操作,其存储结构为:typedef struct Node{int dest; //邻接表的弧头结点序号struct Node *next;}Edge; //邻接表单链表的结点结构体typedef struct{DataType data; //结点数据元素int sorce; //邻接表的弧尾结点序号Edge *adj; //邻接边的头指针int pre; //指向此点的点的序号}AdjLHeight; //数组的数据元素类型结构体typedef struct{AdjLHeight a[10000]; //邻接表数组int numOfVerts; /结点个数int numOfEdges; //边个数}AdjLGraph; //邻接表结构体3.算法设计一侧的野人数目和修道士数目以及船在哪一岸共同构成一种状态,分析一会发现合法的状态是有限且固定的。
C++实现传教士与野人过河问题实验报告
else operation.push_back(" 0
2 | 右岸->左岸");
currentState.churchL = lastcurrentState.churchL;
currentState.wildL = lastcurrentState.wildL - 2 * lastcurrentState.boat;
currentState.boat = -lastcurrentState.boat;
lastParameters.push_back(currentState);
CvsWdfs(currentState, lastParameters,operation, 0);
operation.pop_back();
lastParameters.pop_back(); return 0; }
int main(){ int churchL = 3, wildL = 3, churchR = 0, wildR = 0;//分别用来计算左岸和右岸的传教士
和野人 vector <riverSides> lastParameters;//保存每一步移动操作的两岸传教士、野人人数 vector <string> operation;//保存当前操作的描述
if (lastcurrentState.boat == lastParameters[i].boat) return 0;
} } //检验人数数据合法性 if (lastcurrentState.churchL < 0 || lastcurrentState.wildL < 0 || lastcurrentState.churchR < 0 || lastcurrentState.wildR < 0)
实验二 传教士与野人过河问题
实验二 传教士与野人过河问题一、实验目的理解并熟悉掌握深度优先搜索和广度优先搜索算法。
二、实验内容设有3个传教士和3个野人来到河边,打算乘一只船从左岸到右岸去。
该船的负载能力为两人。
在任何时候,如果野人人数超过传教士人数,野人就会把传教士吃掉。
他们怎样才能用这条船安全的把所有人都渡过河去?三、实验内容(1)设置状态变量并确定值域设M 为传教士人数,C 为野人人数,B 为船数,要求M>=C 且M+C <= 3, L 表示左岸,R 表示右岸。
初始状态目标状态 L RL R M 3 0M 0 3 C 3 0C 0 3 B 1 0B 0 1(2)确定状态组,分别列出初始状态集和目标状态集用三元组来表示f S :(ML , CL , BL )(均为左岸状态) 其中,03,03ML CL ≤≤≤≤,BL ∈{ 0 , 1}0S :(3 , 3 , 1) g S : (0 , 0 , 0)初始状态0S :表示全部成员在河的的左岸;目标状态g S :表示全部成员从河的左岸全部渡河到河的右岸。
(3)定义并确定规则集合 以河的左岸为基点来考虑,把船从左岸划向右岸定义为Pij 操作。
其 中,第一下标i 表示船载的传教士数,第二下标j 表示船载的食人者数;同理,从右岸将船划回左岸称之为Qij操作,下标的定义同前。
则共有10种操作,操作集为: F={P01,P10,P11,P02,P20,Q01,Q10,Q11,Q02,Q20}if ( ML ,CL , BL=1 ) then ( ML–1 , CL , BL –1 ) P10Pif ( ML ,CL , BL=1 ) then ( ML , CL–1 , BL –1 ) 01if ( ML ,CL , BL=1 ) then ( ML–1 , CL–1 , BL –1 ) P11Pif ( ML ,CL , BL=1 ) then ( ML–2 , CL , BL –1 ) 20if ( ML ,CL , BL=1 ) then ( ML , CL–2 , BL –1 ) P02if ( ML ,CL , BL=0 ) then ( ML+1 , CL , BL+1 ) Q10Qif ( ML ,CL , BL=0 ) then ( ML , CL+1 , BL +1 ) 01if ( ML ,CL , BL=0 ) then ( ML+1 , CL +1, BL +1 ) Q11if ( ML ,CL , BL=0 ) then ( ML+2 , CL +2, BL +1 ) Q20if ( ML ,CL , BL=0 ) then ( ML , CL +2, BL +1 ) Q02(4)当状态数量不是很大时,画出合理的状态空间图箭头旁边所标的数字表示了P或Q操作的下标,即分别表示船载的传教士数和野人数。
野人和传教士过河问题的C语言源代码
野⼈和传教⼠过河问题的C语⾔源代码 1//问题:有3个传教⼠和3个野⼈要过河,只有⼀艘船,这艘船每次2//只能载2个⼈过河,且⽆论哪边野⼈的数量⼤于传教⼠的数量时,3//野⼈就会吃掉传教⼠。
怎样让他们都安全过河?45 #include <stdio.h>6 #include <string.h>78#define STEP_MAX 20 //来回过河的次数9#define KIND_NUM 3 //每个种类的数量10#define BOAT_NUM 2 //船的载重量1112 typedef enum13 {14 BOAT_THIS,//船在本岸15 BOAT_THAT,//船在对岸16 } boat_t;1718 typedef enum19 {20 ROAD_GO,//过河21 ROAD_COME,//回来22 } road_t;2324 typedef struct25 {26int ye;//对岸野⼈数量27int man;//对岸传教⼠数量28 boat_t boat;//船是否在对岸29 } state_t;//⼀种局⾯3031 typedef struct32 {33int ye;//野⼈过河数量34int man;//传教⼠过河的数量35 road_t road;//回来或过河36 } step_t;//⼀个步骤373839 state_t states[STEP_MAX] = { 0 };40 step_t steps[STEP_MAX] = { 0 };414243//判断所有的野⼈和传教⼠是否都到了对岸44bool final(state_t s)45 {46const state_t cs =47 {48 KIND_NUM,49 KIND_NUM,50 BOAT_THAT51 };52if (memcmp(&cs, &s, sizeof(state_t)) == 0)53 {54return true;55 }56return false;57 }5859//是否会发⽣野⼈杀传教⼠60bool bad(state_t s)61 {62if (((KIND_NUM - s.ye) > (KIND_NUM - s.man)63 && (KIND_NUM - s.man) > 0)64 || (s.ye > s.man&& s.man > 0))65 {66return true;67 }68return false;69 }7071//第n种局⾯是否跟前⾯的相重复72bool repeat(state_t s[], int n)73 {74int i;7576for (i = 0; i < n; i++)77 {//已经有这种情况了78if (memcmp(&states[i], &states[n], sizeof(states[i])) == 0)79 {80return true;81 }82 }83return false;84 }8586void output(step_t steps[STEP_MAX], int n)87 {88const char* kinds[KIND_NUM] = { "野⼈","传教⼠" };89const char* roads[2] = { "过河.","回来." };90int i;9192for (i = 0; i < n; i++)93 {94if ((steps[i].ye * steps[i].man) > 0)95 {96 printf("%d个%s和%d个%s%s\n", steps[i].ye, kinds[0],97 steps[i].man, kinds[1], roads[steps[i].road]);98 }99else if (steps[i].ye > 0)100 {101 printf("%d个%s%s\n", steps[i].ye, kinds[0],102 roads[steps[i].road]);103 }104else if (steps[i].man > 0)105 {106 printf("%d个%s%s\n", steps[i].man, kinds[1],107 roads[steps[i].road]);108 }109 }110 printf("\n");111 }112113//搜索过河⽅案(n表⽰过河次数)114void search(int n)115 {116int i, j;117118if (n > STEP_MAX)119 {//步数限制太少了120 printf("Step Overflow!");121return;122 }123if (final(states[n]))124 {//都到对岸了125 output(steps, n);126return;127 }128if (bad(states[n]))129 {//发⽣了野⼈杀传教⼠了130return;131 }132if (repeat(states, n))133 {//与前⾯的局⾯相重复了134return;135 }136if (states[n].boat == BOAT_THIS)137 {//船在本岸138for (i = 0; i <= BOAT_NUM && i <= (KIND_NUM - states[n].ye); i++)139for (j = 0; j <= BOAT_NUM - i && j <= (KIND_NUM - states[n].man); j++) 140 {141if (i == 0 && j == 0)142 {143continue;144 }145 steps[n].ye = i;146 steps[n].man = j;147 steps[n].road = ROAD_GO;148 memcpy(&states[n + 1], &states[n], sizeof(state_t));149 states[n + 1].ye += i;150 states[n + 1].man += j;151 states[n + 1].boat = BOAT_THAT;152 search(n + 1);153 }154 }155else156 {157for (i = 0; i <= BOAT_NUM && i <= states[n].ye; i++)158 {159for (j = 0; j <= BOAT_NUM - i && j <= states[n].man; j++)160 {161if (i == 0 && j == 0)162 {163continue;164 }165 steps[n].ye = i;166 steps[n].man = j;167 steps[n].road = ROAD_COME;168 memcpy(&states[n + 1], &states[n], sizeof(state_t)); 169 states[n + 1].ye -= i;170 states[n + 1].man -= j;171 states[n + 1].boat = BOAT_THIS;172 search(n + 1);173 }174 }175 }176 }177178int main()179 {180 search(0);181return0;182 }1个野⼈和1个传教⼠过河.1个传教⼠回来.2个野⼈过河.1个野⼈回来.2个传教⼠过河.1个野⼈和1个传教⼠回来.2个传教⼠过河.1个野⼈回来.2个野⼈过河.1个传教⼠回来.1个野⼈和1个传教⼠过河.1个野⼈和1个传教⼠过河.1个传教⼠回来.2个野⼈过河.1个野⼈回来.2个传教⼠过河.1个野⼈和1个传教⼠回来.2个传教⼠过河.1个野⼈回来.2个野⼈过河.1个野⼈回来.2个野⼈过河.2个野⼈过河.1个野⼈回来.2个野⼈过河.1个野⼈回来.2个传教⼠过河.1个野⼈和1个传教⼠回来.2个传教⼠过河.1个野⼈回来.2个野⼈过河.1个传教⼠回来.1个野⼈和1个传教⼠过河.2个野⼈过河.1个野⼈回来.2个野⼈过河.1个野⼈回来.2个传教⼠过河.1个野⼈和1个传教⼠回来.2个传教⼠过河.1个野⼈回来.2个野⼈过河.1个野⼈回来.2个野⼈过河.。
人工智能实验2传教士过河问题
人工智能实验报告班级:计研-12班学号:2012312120105 姓名:孔德星实验二知识表示方法1.实验目的(1)了解知识表示相关技术;(2)掌握问题规约法或者状态空间法的分析方法。
2.实验内容(2个实验内容可以选择1个实现)(1)梵塔问题实验。
熟悉和掌握问题规约法的原理、实质和规约过程;理解规约图的表示方法;(2)状态空间法实验。
从前有一条河,河的左岸有m个传教士、m个野人和一艘最多可乘n人的小船。
约定左岸,右岸和船上或者没有传教士,或者野人数量少于传教士,否则野人会把传教士吃掉。
搜索一条可使所有的野人和传教士安全渡到右岸的方案。
3.实验报告要求(1)简述实验原理及方法,并请给出程序设计流程图。
实验原理:假设开始时传教士、野人和船都在右岸,用数组(a,b,c)分别表示右岸传教士个数、右岸野人个数、船的位置,则可分为三种情况讨论:A、n>m/2。
此种情况下,先把所有的野人度过去,每次返回一个野人,当出现(m,0,0)情况时,返回m-n个野人(若m==n,返回1个野人)。
然后渡n个传教士,此时野人==传教士,然后返回一个野人和传教士,再开始最大限度的渡传教士,每次返回一个野人,最终直到a==b==c==0;B、n<=3&&n<=m/2 || n==1,显然此时无解;C、n>=4&&n<=m/2,此时只能每次传n/2个传教士和野人,每次返回一个野人和传教士,直到最终结果。
程序流程图:(2)源程序清单:本程序用C++语言编写。
#include"iostream"using namespace std;bool flag = false; //标记是否有解bool af = false; //标记a是否为0bool bf = false; //当b变为0后赋值为true;bool ef = false; //当a==b后赋值为truebool f = false; //判断n是否大于m/2int m;//传教士野人的个数int n;//船一次能装载的人数void mc(int a,int b,int c);int main(){cout<<"传教士与野人过河问题。
传教士和野人问题实验报告
传教士和野人问题实验报告1.上机内容传教士与野人问题求解(宽度搜索算法)二二问题背景:从前有一条河,河的左岸有 m 个传教士(Missionary)和 m 个野人(Cannibal),和一艘最多可乘 n 人的小船。
约定左岸,右岸和船上或者没有传教士,或者野人数量少于传教士,否则野人会把传教士吃掉。
三三实验内容:编程,接收 m 和 n,搜索一条可让所有的野人和传教士安全渡到右岸的方案,例如下图: (M 表示传教士(Missionary),C 表示野人(Cannibal))初态目标 Left Bank River Right bankLeft Bank River Right bank M....M....C....C....注:本实验的举例均以 3 个传教士和 3 个野人同在左岸作为初始状态。
四四实验方案和算法:1 .数据结构:本实验需要用到的数据结构主要是队列和堆栈,其实现均包含于 dso.h 头文件中,分别命名为 class stack 和 class queue。
2 2 .宽度搜索算法:(1) 结点结构:class Move {public:int missionary;//要移动的传教士数量int cannibal;//野人}; class ElemType : Move {//继承 Move 类,获得传教士,野人数据成员。
private:bool boat;//船是否在左岸?public:ElemType_flag;// 标示前一状态即扩展出本结点的父结点信息ElemType(int MA__PEOPLE) {//创建初始状态missionary = cannibal = MA__PEOPLE;boat = true;flag = NULL;} ......在这里,Elemtype 集成了 Move,从而获得 Move 类的传教士和野人数据成员。
以上两个类的数据成员用于保存所有扩展生成的结点。
C++实验报告[野人过河]
面向对象程序设计实验一、实验题目:四个修道士与四个野人过河问题。
二、实验内容:运用面向对象的思想,定义类描述有限状态自动机,状态的输入和输出关系可以描述为链表数据成员,用C++ builder 6.0 编程序;使用有限自动机编程解决四个修道士与四个野人过河问题:有四个修道士和四个野人要过河,假定船最多只能载三个修道士或者野人,野人服从修道士的指挥,无论何时,只要野人多于修道士,野人就会吃掉修道士,以有限自动机为基础,编程解决四个修道士与四个野人的过河问题;用C++ builder 6.0 编程序。
三、实验要求:必须运用面向对象的思想进行类型设计;流程清晰,缩进编排,格式美观,注解详细;必须采用如下类型作为状态转移节点和状态类型,可在状态转移节点和状态类型基础上设计新类型:class STA TE;class LIST{ //状态转移链表类LIST *next;char i nput;STA TE *output;LIST(char in, STA TE *out); //私有,仅供STA TE 使用~LIST( );friend STA TE;};class STA TE{ //状态类char *name; //状态名LIST *list; //输入及输出状态转移列表static STA TE*error;public:void enlist(char in, STA TE *out); //插入listconst STA TE *next(char in)const; //输入in 转移到下一个状态const STA TE *start(char *)const; //启动有限自动机STA TE(char *name);~STA TE( );};四、类型设计:LIST 类:LIST *next:是指向下一个状态转移节点的指针;char input:是状态转移的算符,不同的状态根据所输入的不同状态转移到相应的新状态,从而实现状态的转移;STA TE *output:新状态的指针,指向根据不同的输入算符而转移到的相应的跳转状态;friend STA TE:将STA TE 作为LIST 类的友元类;STA TE 类:char *name:状态的名称;LIST *list:当前状态的邻接链表的头指针,每个状态都用这个指针指向从本状态输入不同运算符而能达到的多有邻接状态的相应LIST 节点;static STA TE *error:错误状态指针;void enlist(char in, STA TE *out):对不同的状态节点建立相应的状态转移链表的方法;const STA TE *next(char in)const:根据对某状态的给定输入在其转台转移链表中查找相应转移状态的方法,返回下一状态的指针;const STA TE *start(char *)const:根据实验者给定的测试路径串(运算符串)进行自动的状态转移,返回最终状态的指针;运算符;B:一个修道士和一个野人过河;W:一个野人过河;T:两个野人过河;Q:三个野人过河;S:一个修道士过河;R:两个修道士和一个野人过河;P:三个修道士过河;D:两个修道士过河;状态命名规则:船在左岸为L,在右岸为R,M 表示修道士,Y 表示野人,走岸和右岸的修道士和野人的数量分别用L 或R 分开,例:M3Y3LM1Y1 表示:船在左岸,左岸有3 个修道士和三个野人,右岸有一个修道士和一个野人。
数据结构实验五——修道士野人问题
( 3 3 1 )船上人数[修道士,野人][3 1]右边岸上[0 2 0]
( 0 2 0 )船上人数[修道士,野人][2 0]左边岸上[2 2 1]
( 2 2 1 )船上人数[修道士,野人][2 2]右边岸上[0 0 0]
( 0 0 0 )
( 3 3 1 )船上人数[修道士,野人][3 1]右边岸上[0 2 0]
重复以上方式输出所有的渡河方式
五、运行测试与分析
输入
输出
实际
1 2
( 1 1 1 )船上人数[修道士,野人][1 1]右边岸上[0 0 0]
( 0 0 0 )
√
3 5
( 3 3 1 )船上人数[修道士,野人][3 2]右边岸上[0 1 0]
( 0 1 0 )船上人数[修道士,野人][1 0]左边岸上[1 1 1]
( 1 1 0 )船上人数[修道士,野人][1 1]左边岸上[2 2 1]
( 2 2 1 )船上人数[修道士,野人][2 2]右边岸上[0 0 0]
( 0 0 0 )
( 3 3 1 )船上人数[修道士,野人][2 2]右边岸上[1 1 0]
( 1 1 0 )船上人数[修道士,野人][2 1]左边岸上[3 2 1]
实验报告五
实验课名称:数据结构与程序设计实验
实验名称:修道士野人问题
班级:
学号:
姓名:
时间:
一、问题描述
河的左岸有N个野人和N个修道士以及一条小船,修道士们想用这条小船把所有的人都运到河的右岸,但又受到以下限制:
所有人都会划船,但船一次只能载C人
在任意岸上,野人数不能超过修道士数,否则修道士将会被野人吃掉。
struct Node *next;
人工智能+野人传教士过河问题c程序+深度优先
open[top].m=M;
open[top].c=C;
open[top].s=S;
open[top].f=0;
open[top].pre=0;
inist();
find(pHead);
system("PAUSE");
{
flag=0;
break;
}
pTemp=pTemp->pre;
}
if(flag)
{
j++;
boatState[j].m = m;
boatState[j].c = c;
}
}
}
}
//*******************************************************************************
int goal(struct state * p) //判断当前状态是否目标状态
{
if(p->m==0&&p->c==0&&p->s==1) return 1;
else return 0;
}
//******************************************************************************
//初始状态:左岸,3野人,3牧师;
// 右岸,0野人,0牧师;
// 船停在左岸,船上有0个人
//目标状态:左岸,0野人,0牧师;
// 右岸,3野人,3牧师;
// 船停在右岸,船上有0个人;
传教士过河问题(实验二)
传教士过河问题实验报告计研-12 2012312120115 宋玲玲1.实验目的(1)了解知识表示相关技术;(2)掌握问题规约法或者状态空间法的分析方法。
2.实验内容从前有一条河,河的左岸有m个传教士、m个野人和一艘最多可乘n人的小船。
约定左岸,右岸和船上或者没有传教士,或者野人数量少于传教士,否则野人会把传教士吃掉。
搜索一条可使所有的野人和传教士安全渡到右岸的方案。
3.实验原理及方法假设开始时传教士、野人和船都在右岸,用数组(a,b,c)分别表示右岸传教士个数、右岸野人个数、船的位置,则可分为三种情况讨论:A、n>m/2。
此种情况下,先把所有的野人度过去,每次返回一个野人,当出现(m,0,0)情况时,返回m-n个野人(若m==n,返回1个野人)。
然后渡n个传教士,此时野人==传教士,然后返回一个野人和传教士,再开始最大限度的渡传教士,每次返回一个野人,最终直到a==b==c==0;B、n<=3&&n<=m/2 || n==1,显然此时无解;C、n>=4&&n<=m/2,此时只能每次传n/2个传教士和野人,每次返回一个野人和传教士,直到最终结果。
程序流程图如下:4、源程序清单:本程序用C++语言编写。
#include"iostream"using namespace std;bool flag = false; //标记是否有解bool af = false; //标记a是否为0bool bf = false; //当b变为0后赋值为true;bool ef = false; //当a==b后赋值为truebool f = false; //判断n是否大于m/2int m;//传教士野人的个数int n;//船一次能装载的人数void mc(int a,int b,int c);int main(){cout<<"传教士与野人过河问题。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
传教士与野人过河问题实验报告
1 问题定义
河的两岸有三个传教士和三个野人需要过河,目前只有一条能装下两个人的船,在河的任何一方或者船上,如果野人的人数大于传教士的人数,那么传教士就会被野人攻击,怎么找出一种安全的渡河方案呢
2 算法分析
首先,先来看看问题的初始状态和目标状态,定义河的两岸分别为左岸和右岸,设定状态集合为(左岸传教士人数,右岸野人数,右岸传教士人数,右岸野人数,船的位置),船的位置:-1表示船在左岸,1表示船在右岸。
初始状态:(3,3,0,0,0,-1)
目标状态:(0,0,3,3,1)
然后,整个问题就抽象成了怎样从初始状态经中间的一系列状态达到目标状态。
问题状态的改变是通过划船渡河来引发的,所以合理的渡河操作就成了通常所说的算符,根据题目要求,可以得出以下5个算符(按照渡船方向的不同,也可以理解为10个算符):渡1野人、渡1传教士、渡1野人1传教士、渡2野人、渡2传教士
根据船的位置,向左移或向右移通过递归依次执行5种算符,判断是否找到所求,并排除不符合实际的状态,就可以找到所有可能的解,如图1所示为递归函数流程图。
数据结构方面采用如下所示的结构体存储当前传教士、野人、船三者的状态。
struct riverSides {
int churchL;ildL == &&lastParameters[i].churchL ==
{
if == lastParameters[i].boat)
return 0;
}
}
//检验人数数据合法性
if < 0 || < 0 || < 0 || < 0)
return 0;
//传教士是否被吃
if ( < && != 0) || < && != 0))
return 0;
//递归执行五类过河操作,boat=-1船在左岸,boat=1船在右岸,传入boat为上一次船位置//下次应当取反
riverSides currentState;
//两个传教士过河
if == 1)
(" 2 0 | 左岸->右岸");
else
(" 2 0 | 右岸->左岸");
= - 2 * ;
= ;
= + 2 * ;
= ;
= ;
(currentState);
CvsWdfs(currentState, lastParameters,operation, 0);
();
();
//两个野人过河
if == 1)
(" 0 2 | 左岸->右岸");
else
(" 0 2 | 右岸->左岸");
= ;
= - 2 * ;
= ;
= + 2 * ;
= ;
(currentState);
CvsWdfs(currentState, lastParameters, operation, 0);
();
();
//一个野人,一个传教士
if == 1)
(" 1 1 | 左岸->右岸");
else
(" 1 1 | 右岸->左岸");
= - 1 * ;
= - 1 * ;
= + 1 * ;
= + 1 * ;
= ;
(currentState);
CvsWdfs(currentState, lastParameters,operation, 0);
();
();
//一个传教士过河
if == 1)
(" 1 0 | 左岸->右岸");
else
(" 1 0 | 右岸->左岸");
= - 1 * ;
= ;
= + 1 * ;
= ;
= ;
(currentState);
CvsWdfs(currentState, lastParameters, operation, 0);
();
();
//一个野人过河
if == 1)
(" 0 1 | 左岸->右岸");
else
(" 0 1 | 右岸->左岸");
= ;
= - 1 * ;
= ;
= + 1 * ;
= ;
(currentState);
CvsWdfs(currentState, lastParameters, operation, 0);
();
();
return 0;
}
int main(){
int churchL = 3, wildL = 3, churchR = 0, wildR = 0;//分别用来计算左岸和右岸的传教士和野人vector <riverSides> lastParameters;//保存每一步移动操作的两岸传教士、野人人数
vector <string> operation;//保存当前操作的描述
//初始化左岸参数,可以认为是从右岸移动至左岸的操作
//boat=-1 表示船在左岸,boat=1表示船在右岸
riverSides currentState;
= 3;
= 3;
= 0;
= 0;
= 1;
(currentState);
CvsWdfs(currentState, lastParameters,operation, 0);
();
system("pause");
return 0;
}
4 程序结果
最终得到如图2、3所示的四种过河方式。
图 2 过河方式1、2
图 3 过河方式3、4。