农夫过河问题
实习六-农夫过河问题
农夫过河问题一、需求分析1.问题描述:一个农夫带着一只狼、一只羊和一棵白菜,身处河的南岸。
他要把这些东西全部运到北岸。
他面前只有一条小船,船只能容下他和一件物品,另外只有农夫才能撑船。
如果农夫在场,则狼不能吃羊,羊不能吃白菜,否则狼会吃羊,羊会吃白菜,所以农夫不能留下羊和白菜自己离开,也不能留下狼和羊自己离开,而狼不吃白菜。
2.基本要求:(1)利用图的存储结构(2)图的搜索算法(3)求出农夫将所有的东西运过河的所有方案二、设计1. 设计思想(1)存储结构以邻接矩阵存储合理状态,用一个一维数组保存所有的方案;(2)主要算法基本思想人,狼,羊和白菜共有2的四次方16种状态(河岸的状态可由人的状态确定),去掉不允许出现的6种状态,10个状态对应矩阵的10个结点值,然后根据状态的连续改变初始化矩阵,接着就用递归的深度优先法搜索所有的路径,即求出过河的方案。
main2. 设计表示(1)函数调用关系图main→Judge→Initiate→DFS→Push→StackPop→Top→GetTop→printfpath→Ch(2)函数接口规格说明int Judge(int a,int b) //将16种状态通过a,b输入,去掉不允许的6种int GetTop(Path *path,int *m) //出栈int Push(Path *path,int m) //入栈int Top(Path *path ,int *m) //读出栈顶值void Initiate(AdjMGraph *G,int n)//邻接矩阵顶点数为n的邻接矩阵G的建立int DFS(AdjMGraph *G,Path *path,int x,int t) 图G中搜索的起始点为X,从t点开始搜索与x关联的顶点,搜索过的点入栈path。
int printfpath(Path *path) //复制出出栈path中所有值,用FA【】保存void Printf(AdjMGraph *G)//辅助:邻接矩阵输出,用于观察搜索的过程。
数据结构实验-农夫过河问题
农夫过河问题一、实验目的掌握广度优先搜索策略,并用队列求解农夫过河问题二、实验内容问题描述:一农夫带着一只狼,一只羊和一颗白菜,身处河的南岸,他要把这些东西全部运到北岸,遗憾的是他只有一只小船,小船只能容下他和一件物品。
这里只能是农夫来撑船,同时因为狼吃羊、羊吃白菜、所以农夫不能留下羊和狼或羊和白菜在河的一边,而自己离开;好在狼属肉食动物,不吃白菜。
农夫怎么才能把所有的东西安全运过河呢?实验要求如下:(1)设计物品位置的表示方法和安全判断算法;(2)设计队列的存储结构并实现队列的基本操作(建立空队列、判空、入队、出队、取对头元素),也可以使用STL中的队列进行代码的编写;(3)采用广度优先策略设计可行的过河算法;(4)输出要求:按照顺序输出一种可行的过河方案;提示:可以使用STL中的队列进行代码编写。
程序运行结果:二进制表示:1111011011100010101100011001,0000三、农夫过河算法流程⏹Step1:初始状态0000入队⏹Step2:当队列不空且没有到达结束状态1111时,循环以下操作:⏹队头状态出队⏹按照农夫一个人走、农夫分别带上三个物品走,循环以下操作:⏹农夫和物品如果在同一岸,则计算新的状态⏹如果新状态是安全的并且是没有处理过的,则更新path[ ],并将新状态入队⏹当状态为1111时,逆向输出path[ ]数组附录一:STL中队列的使用注:队列,可直接用标准模板库(STL)中的队列。
需要#include<queue>STL中的queue,里面的一些成员函数如下(具体可以查找msdn,搜索queue class):front:Returns a reference to the first element at the front of the queue.pop:Removes an element from the front of the queuepush:Adds an element to the back of the queueempty:Tests if the queue is empty三、实验代码FarmerRiver.H#ifndef FARMERRIVER_H#define FARMERRIVER_Hint FarmerOnRight(int status); //农夫,在北岸返回1,否则返回0int WorfOnRight(int status); //狼int CabbageOnRight(int status); //白菜int GoatOnRight(int status); //羊int IsSafe(int status); //判断状态是否安全,安全返回1,否则返回0void FarmerRiver();#endifSeqQueue.h#ifndef SEQQUEUE_H#define SEQQUEUE_Htypedef int DataType;struct Queue{int Max;int f;int r;DataType *elem;};typedef struct Queue *SeqQueue;SeqQueue SetNullQueue_seq(int m);int IsNullQueue_seq(SeqQueue squeue);void EnQueue_seq(SeqQueue squeue, DataType x);void DeQueue_seq(SeqQueue);DataType FrontQueue_seq(SeqQueue);#endifFarmerRiver.c#include <stdio.h>#include <stdlib.h>#include "SeqQueue.h"#include "FarmerRiver.h"int FarmerOnRight(int status) //判断当前状态下农夫是否在北岸{return (0!=(status & 0x08));}int WorfOnRight(int status){return (0!=(status & 0x04));}int CabbageOnRight(int status){return (0!=(status & 0x02));}int GoatOnRight(int status){return (0!=(status & 0x01));}int IsSafe(int status) //判断当前状态是否安全{if ((GoatOnRight(status)==CabbageOnRight(status)) &&(GoatOnRight(status)!=FarmerOnRight(status)))return (0); //羊吃白菜if ((GoatOnRight(status)==WorfOnRight(status)) && (GoatOnRight(status)!=FarmerOnRight(status))) return 0; //狼吃羊return 1; //其他状态是安全的}void FarmerRiver(){int i, movers, nowstatus, newstatus;int status[16]; //用于记录已考虑的状态路径SeqQueue moveTo;moveTo = SetNullQueue_seq(20); //创建空列队EnQueue_seq(moveTo, 0x00); //初始状态时所有物品在北岸,初始状态入队for (i=0; i<16; i++) //数组status初始化为-1{status[i] = -1;}status[0] = 0;//队列非空且没有到达结束状态while (!IsNullQueue_seq(moveTo) && (status[15]==-1)){nowstatus = FrontQueue_seq(moveTo); //取队头DeQueue_seq(moveTo);for (movers=1; movers<=8; movers<<=1)//考虑各种物品在同一侧if ((0!=(nowstatus & 0x08)) == (0!=(nowstatus & movers)))//农夫与移动的物品在同一侧{newstatus = nowstatus ^ (0x08 | movers); //计算新状态//如果新状态是安全的且之前没有出现过if (IsSafe(newstatus)&&(status[newstatus] == -1)){status[newstatus] = nowstatus; //记录新状态EnQueue_seq(moveTo, newstatus); //新状态入队}}}//输出经过的状态路径if (status[15]!=-1){printf("The reverse path is: \n");for (nowstatus=15; nowstatus>=0; nowstatus=status[nowstatus]){printf("The nowstatus is: %d\n", nowstatus);if (nowstatus == 0)return;}}elseprintf("No solution.\n");}Sequeue.c#include <stdio.h>#include <stdlib.h>#include "SeqQueue.h"SeqQueue SetNullQueue_seq(int m){SeqQueue squeue;squeue = (SeqQueue)malloc(sizeof(struct Queue));if (squeue==NULL){printf("Alloc failure\n");return NULL;}squeue->elem = (int *)malloc(sizeof(DataType) * m);if (squeue->elem!=NULL){squeue->Max = m;squeue->f = 0;squeue->r = 0;return squeue;}else free(squeue);}int IsNullQueue_seq(SeqQueue squeue){return (squeue->f==squeue->r);}void EnQueue_seq(SeqQueue squeue, DataType x) //入队{if ((squeue->r+1) % squeue->Max==squeue->f) //是否满printf("It is FULL Queue!");else{squeue->elem[squeue->r] = x;squeue->r = (squeue->r+1) % (squeue->Max);}}void DeQueue_seq(SeqQueue squeue) //出队{if (IsNullQueue_seq(squeue))printf("It is empty queue!\n");elsesqueue->f = (squeue->f+1) % (squeue->Max); }DataType FrontQueue_seq(SeqQueue squeue) //求队列元素{if (squeue->f==squeue->r)printf("It is empty queue!\n");elsereturn (squeue->elem[squeue->f]);}main.c#include <stdio.h>#include <stdlib.h>#include "FarmerRiver.h"int main(void){FarmerRiver();return 0;}实验结果:四、实验总结。
农夫过河问题(C++编写)
cout<<"\t 农夫使三样东西平安过河方法为:"<<endl<<endl;
Ferry(0);
}
for (i = 0; i < ferryTimes; i++) {
if (a[i][3] == 0) {
cout<<"\t\t\t 载"<<name[b[i]]<<"到对岸"<<endl; } else {
cout<<"\t\t\t 载"<<name[b[i]]<<"回本岸"<<endl; } } cout<<endl; return; } //狼单独和羊在一起以及羊和白菜单独在一起的情况 if (a[ferryTimes][1] != a[ferryTimes][3] && (a[ferryTimes][2] == a[ferryTimes][1] || a[ferryTimes][0] == a[ferryTimes][1])) { return; }
3)、如果上两个条件都不满,则可执行运输的动作,但每次都应考虑,该运 输情况以前是否执行过(即两岸以及船上的东西以及各自位置和以前完全相同),
农夫过河问题(C++编写)
cout<<" **
------------------------
**\n";
cout<<" **
|
农夫过河问题
|
**\n";
cout<<" **
------------------------
**\n";
cout<<"
****************************************************************************\n";
1、问题描述
从前,一个农夫带着一只狼,一只羊和一棵白菜要河(注意该狼被农夫训服了,但还会 吃羊)。他要将所有东西安全的带到河的对岸,不幸的是河边只有一条船,只能装下农夫和 他的一样东西,并且农夫必须每次都随船过,因为只有他能撑船。在无人看管的情况下,狼 要吃羊,羊要吃白菜,因此,农夫不能在河的某边岸上单独留下狼和羊,也不能单独留下羊 和白菜。那么农夫如何才能使三样东西平安过河呢?
3)、如果上两个条件都不满,则可执行运输的动作,但每次都应考虑,该运 输情况以前是否执行过(即两岸以及船上的东西以时四者各自的状态,并递归的进行下一次运载。
5、系统测试
6、经验总结
解决实际问题时,应先分析实际问题,找出实际问题的所有约束条件, 然后对问题进行数学模型的抽象化,抓主要因素,省去一些不需要的因素,将其 抽象为数学问题,然后再从整体上设计算法,搭建程序的框架,最后一步步完善 细节,这样做,会使本来毫无头绪的问题变得清晰起来。
if (a[ferryTimes][i] == a[ferryTimes][3]) {
题目 流程农夫过河
题目流程农夫过河一、基础过河规则类题目(1 - 5题)题目1:农夫带着狼、羊和一筐白菜要过河。
只有一条小船,农夫每次只能带一样东西过河。
如果农夫不在,狼会吃羊,羊会吃白菜。
请问农夫怎样才能安全地把狼、羊和白菜都运到河对岸?解析:1. 农夫先把羊运到河对岸,然后农夫独自返回。
- 原因是狼不吃白菜,这样河这边留下狼和白菜是安全的。
2. 农夫再把狼运到河对岸,然后农夫带着羊返回。
- 因为如果不把羊带回来,狼会吃羊。
3. 农夫把白菜运到河对岸,然后农夫独自返回。
- 此时河对岸有狼和白菜,是安全的。
4. 最后农夫把羊运到河对岸。
题目2:农夫要带狐狸、鸡和一袋米过河。
船很小,农夫每次只能带一个东西过河。
如果农夫不在,狐狸会吃鸡,鸡会吃米。
农夫应该怎样安排过河顺序?解析:1. 农夫先把鸡运到河对岸,然后农夫独自返回。
- 这样河这边留下狐狸和米是安全的。
2. 农夫再把狐狸运到河对岸,然后农夫带着鸡返回。
- 防止狐狸吃鸡。
3. 农夫把米运到河对岸,然后农夫独自返回。
- 此时河对岸有狐狸和米,安全。
4. 最后农夫把鸡运到河对岸。
题目3:农夫带着狗、兔子和一篮胡萝卜过河。
船只能载农夫和一样东西。
若农夫不在,狗会咬兔子,兔子会吃胡萝卜。
怎样安全过河?解析:1. 农夫先带兔子过河,然后独自返回。
- 因为狗不吃胡萝卜,这样河这边狗和胡萝卜是安全的。
2. 农夫再带狗过河,然后带兔子返回。
- 避免狗咬兔子。
3. 农夫带胡萝卜过河,然后独自返回。
- 此时河对岸狗和胡萝卜安全。
4. 最后农夫带兔子过河。
题目4:有个农夫要带蛇、鼠和一袋谷子过河,船每次只能载农夫和一样东西。
农夫不在时,蛇会吃鼠,鼠会吃谷子。
如何安全渡河?解析:1. 农夫先带鼠过河,然后独自返回。
- 此时河这边蛇和谷子是安全的。
2. 农夫再带蛇过河,然后带鼠返回。
- 防止蛇吃鼠。
3. 农夫带谷子过河,然后独自返回。
- 河对岸蛇和谷子安全。
4. 最后农夫带鼠过河。
题目5:农夫带着猫、鱼和一盆花过河。
数据结构实验-农夫过河问题
农夫过河问题一、实验目的掌握广度优先搜索策略,并用队列求解农夫过河问题二、实验内容问题描述:一农夫带着一只狼,一只羊和一颗白菜,身处河的南岸,他要把这些东西全部运到北岸,遗憾的是他只有一只小船,小船只能容下他和一件物品。
这里只能是农夫来撑船,同时因为狼吃羊、羊吃白菜、所以农夫不能留下羊和狼或羊和白菜在河的一边,而自己离开;好在狼属肉食动物,不吃白菜。
农夫怎么才能把所有的东西安全运过河呢?实验要求如下:(1)设计物品位置的表示方法和安全判断算法;(2)设计队列的存储结构并实现队列的基本操作(建立空队列、判空、入队、出队、取对头元素),也可以使用STL中的队列进行代码的编写;(3)采用广度优先策略设计可行的过河算法;(4)输出要求:按照顺序输出一种可行的过河方案;提示:可以使用STL中的队列进行代码编写。
程序运行结果:二进制表示:1111011011100010101100011001,0000三、农夫过河算法流程⏹Step1:初始状态0000入队⏹Step2:当队列不空且没有到达结束状态1111时,循环以下操作:⏹队头状态出队⏹按照农夫一个人走、农夫分别带上三个物品走,循环以下操作:⏹农夫和物品如果在同一岸,则计算新的状态⏹如果新状态是安全的并且是没有处理过的,则更新path[ ],并将新状态入队⏹当状态为1111时,逆向输出path[ ]数组附录一:STL中队列的使用注:队列,可直接用标准模板库(STL)中的队列。
需要#include<queue>STL中的queue,里面的一些成员函数如下(具体可以查找msdn,搜索queue class):front:Returns a reference to the first element at the front of the queue.pop:Removes an element from the front of the queuepush:Adds an element to the back of the queueempty:Tests if the queue is empty三、实验代码FarmerRiver.H#ifndef FARMERRIVER_H#define FARMERRIVER_Hint FarmerOnRight(int status); //农夫,在北岸返回1,否则返回0int WorfOnRight(int status); //狼int CabbageOnRight(int status); //白菜int GoatOnRight(int status); //羊int IsSafe(int status); //判断状态是否安全,安全返回1,否则返回0void FarmerRiver();#endifSeqQueue.h#ifndef SEQQUEUE_H#define SEQQUEUE_Htypedef int DataType;struct Queue{int Max;int f;int r;DataType *elem;};typedef struct Queue *SeqQueue;SeqQueue SetNullQueue_seq(int m);int IsNullQueue_seq(SeqQueue squeue);void EnQueue_seq(SeqQueue squeue, DataType x);void DeQueue_seq(SeqQueue);DataType FrontQueue_seq(SeqQueue);#endifFarmerRiver.c#include <stdio.h>#include <stdlib.h>#include "SeqQueue.h"#include "FarmerRiver.h"int FarmerOnRight(int status) //判断当前状态下农夫是否在北岸{return (0!=(status & 0x08));}int WorfOnRight(int status){return (0!=(status & 0x04));}int CabbageOnRight(int status){return (0!=(status & 0x02));}int GoatOnRight(int status){return (0!=(status & 0x01));}int IsSafe(int status) //判断当前状态是否安全{if ((GoatOnRight(status)==CabbageOnRight(status)) && (GoatOnRight(status)!=FarmerOnRight(status)))return (0); //羊吃白菜if ((GoatOnRight(status)==WorfOnRight(status)) && (GoatOnRight(status)!=FarmerOnRight(status))) return 0; //狼吃羊return 1; //其他状态是安全的}void FarmerRiver(){int i, movers, nowstatus, newstatus;int status[16]; //用于记录已考虑的状态路径SeqQueue moveTo;moveTo = SetNullQueue_seq(20); //创建空列队EnQueue_seq(moveTo, 0x00); //初始状态时所有物品在北岸,初始状态入队for (i=0; i<16; i++) //数组status初始化为-1{status[i] = -1;}status[0] = 0;//队列非空且没有到达结束状态while (!IsNullQueue_seq(moveTo) && (status[15]==-1)){nowstatus = FrontQueue_seq(moveTo); //取队头DeQueue_seq(moveTo);for (movers=1; movers<=8; movers<<=1)//考虑各种物品在同一侧if ((0!=(nowstatus & 0x08)) == (0!=(nowstatus & movers)))//农夫与移动的物品在同一侧{newstatus = nowstatus ^ (0x08 | movers); //计算新状态//如果新状态是安全的且之前没有出现过if (IsSafe(newstatus)&&(status[newstatus] == -1)){status[newstatus] = nowstatus; //记录新状态EnQueue_seq(moveTo, newstatus); //新状态入队}}}//输出经过的状态路径if (status[15]!=-1){printf("The reverse path is: \n");for (nowstatus=15; nowstatus>=0; nowstatus=status[nowstatus]){printf("The nowstatus is: %d\n", nowstatus);if (nowstatus == 0)return;}}elseprintf("No solution.\n");}Sequeue.c#include <stdio.h>#include <stdlib.h>#include "SeqQueue.h"SeqQueue SetNullQueue_seq(int m){SeqQueue squeue;squeue = (SeqQueue)malloc(sizeof(struct Queue));if (squeue==NULL){printf("Alloc failure\n");return NULL;}squeue->elem = (int *)malloc(sizeof(DataType) * m);if (squeue->elem!=NULL){squeue->Max = m;squeue->f = 0;squeue->r = 0;return squeue;}else free(squeue);}int IsNullQueue_seq(SeqQueue squeue){return (squeue->f==squeue->r);}void EnQueue_seq(SeqQueue squeue, DataType x) //入队{if ((squeue->r+1) % squeue->Max==squeue->f) //是否满printf("It is FULL Queue!");else{squeue->elem[squeue->r] = x;squeue->r = (squeue->r+1) % (squeue->Max);}}void DeQueue_seq(SeqQueue squeue) //出队{if (IsNullQueue_seq(squeue))printf("It is empty queue!\n");elsesqueue->f = (squeue->f+1) % (squeue->Max); }DataType FrontQueue_seq(SeqQueue squeue) //求队列元素{if (squeue->f==squeue->r)printf("It is empty queue!\n");elsereturn (squeue->elem[squeue->f]);}main.c#include <stdio.h>#include <stdlib.h>#include "FarmerRiver.h"int main(void){FarmerRiver();return 0;}实验结果:四、实验总结。
农夫过河问题
农夫过河问题
一、先分析农夫过河的情景:1.他走到了小桥上,遇见了大象;2.他看见小桥很窄,不能通过大象,于是下来,又看见了小兔子;3.小兔子让他再回去把自己带来的萝卜给小猴子送去;4.他想了想,就决定去找乌龟帮忙。
二、农夫为什么要这样做?我们可以用图中所示的几种方法来解答:(1)如果你是农夫,你会怎么办呢?(2)我们在学习时也常常有这样的问题,面对某个复杂的问题,总是从多角度考虑它,然后得出最佳的解决方案。
比如我们要学好数学,需要同学之间互相讨论交流,取长补短,共同进步。
三、根据刚才提供的信息和已经确立的条件,你认为哪些条件更重要?请写出两点理由并说明原因。
四、结合生活实际谈谈应该怎样正确处理人与人之间的关系。
宽搜农夫过河问题
农夫过河问题宽搜(bfs)算法详解农夫过河问题(农夫、狼、羊和白菜的问题),描述如下:一个农夫,带着一只狼、一只羊、和一棵白菜,身处河的南岸,他要把这些东西全部运到北岸。
农夫的面前有一条小船,船小到只能容下他和一件物件。
另外,只能农夫会撑船。
又因为狼能吃羊,而羊爱吃白菜,所以农夫不能留下羊和白菜而自己离开,也不能留下狼和羊而自己离开。
但狼属于食肉动物,不吃白菜。
问农夫采取什么方案才能将所有的东西运过河?解题思路农夫过河问题的求解方法是使用广度优先搜索(BFS),即在搜索过程中总是最先搜索下面一步的所有可能状态,然后再进行考虑更后面的各种情况。
要实现广度优先搜索,一般采用队列结构。
把下一步所有可能达到的状态都列举出来,放在这个队列中,然后顺序取出来分别对其进行处理,处理过程中再把下一步的状态放在队列里。
在采用编程解决农夫过河的问题时,首先需要考虑以下几个问题:•程序中为了方便描述农夫过河过程中几个角色的位置(位于南岸还是北岸),最好的方法是用4 个二进制数,分别顺序表示农夫、狼、白菜和羊的位置。
在本节程序中,用二进制0 表示某角色在河的南岸,用 1 表示某角色在河的北岸。
例如,整数5(其二进制为0101),表示农夫和白菜在河的南岸,而狼和羊在北岸。
•为了方便获取各个角色当前所在的位置,程序中设置了如下 4 个函数。
其中,函数返回值为1,反之则表示角色在河的北岸://表示农夫状态的函数,返回0 ,表示农夫在南岸,反之在北岸。
int farmer(int location){return(0!=(location & 0x08));}//表示狼的状态的函数,返回0 ,表示农夫在南岸,反之在北岸int wolf(int location){return(0!=(location & 0x04));}//表示白菜状态的函数,返回0 ,表示农夫在南岸,反之在北岸int cabbage(int location){return(0!=(location & 0x02));}//表示羊状态的函数,返回0 ,表示农夫在南岸,反之在北岸int goat(int location){return(0!=(location & 0x01));}其中,location 为当前4 种角色所处的状态,其值为0(0000)到15(1111)之间的数。
农夫过河问题
农夫过河问题1. 题目描述:一个农夫带着一只狼,一只羊和一筐菜,欲从河的左岸坐船到右岸,由于船太小,农夫每次只能带一样东西过河,并且没有农夫看管的话,狼会吃掉羊,羊会吃菜。
设计一个方案,使农夫可以无损失的过河2. 题目分析:假设人、狼、菜、羊都在河岸a,要到b 河岸去。
题中的食物链关系为: 菜→羊→狼 所以,第一次人只能带羊到b 河岸; 回到a 时,人不能再将刚带过来的羊带回去,所以人是空手回到a 的; 在a 河岸,人有两个选择选择一:(1) 带狼到b,人再回到a 时,因为不能把狼和羊同时留下,所以只能带走羊;AA 羊 A B羊狼 菜 怎么办呢 B 羊 B 狼 菜 菜 狼(2) 再次回到a 后,人再到b 时,不能把羊和菜同时留下,所以只能带走菜; (3) 再次回到a 时,因为狼和菜可以同时留下,所以优先选择空手过河;到a 后发现只剩下羊,所以带羊过河。
选择二:(1) 带菜到b,人再回到a 时,因为不能把菜和羊同时留下,所以只能带走羊;(2) 再次回到a 后,人再到b 时,不能把羊和狼同时留下,所以只能带走狼;狼 羊 羊 A菜 B 羊 狼 A B 狼 AB 狼 AB 羊菜 菜 菜(3) 再次回到a 时,因为狼和菜可以同时留下,所以优先选择空手过河;到a 后发现只剩下羊,所以带羊过河。
解:用四元组S 表示状态,即S =(L ,J ,M ,N )其中L :农夫 J :狼 M :羊 N :菜用0表示在左岸岸,1表示在右岸,即S=(0,0,0,0) 目标G =(1,1,1,1)定义操作符L (i )表示农夫带东西到右岸:i=0 农夫自己到右岸;i=1 农夫带狼到右岸;i=2 农夫带羊到右岸;i=3 农夫带菜到右岸;定义操作符R (i )表示农夫带东西到左岸:i=0 农夫自己到左岸;i=1 农夫带狼到左岸;i=2 农夫带羊到左岸;i=3 农夫带菜到左岸;约束状态如下:(1,0,0,1)狼、羊在左岸;(1,1,0,0)羊、菜在左岸;(0,1,1,0)狼、羊在右岸;(0,0,1,1)羊、菜在右岸;(1,0,0,0)狼、羊、菜在左岸;(0,1,1,1)狼、羊、菜在右岸;羊 A B狼 菜。
农夫过河
数据结构课程设计《农夫过河》学院:软件学院班级:12软工移动2班姓名:学号:12151156711 问题描述农夫过河问题是指农夫带一只狼、一只羊和一棵白菜在河南岸, 需要安全运到北岸。
一条小船只能容下他和一件物品, 只有农夫能撑船,问农夫怎么能安全过河?狼吃羊, 羊吃白菜, 农夫不能将这两种或三种物品同时放在河的一侧, 因为没有农夫的照看, 狼就要吃羊, 而羊可能要吃白菜。
这类问题的实质是系统的状态问题, 要寻求的是从初始状态经一系列的安全状态到达系统的终止状态的一条路径。
2概要设计2.1 数据结构的设计农夫过河问题的模型化有一组状态( 如农夫和羊在南, 狼和白菜在北) ; 从一个状态可合法地转到另外几个状态( 如农夫自己过河或农夫带着羊过河) ; 有些状态不安全( 如农夫在北, 其他东西在南) ; 有一个初始状态( 都在南) ; 结束状态( 这里只有一个, 都在北) 。
问题表示: 需要表示问题中的状态, 农夫等位于南和北( 每个有两种可能) 。
可以采用位向量, 4 个二进制位的情况表示状态, 显而易见, 共24= 16种可能状态。
从高位到低位分别表示农夫、狼、白菜和羊。
0000( 整数0) 表示都在南岸, 目标状态1111( 即整数15) 表示都到了北岸。
有些状态0011,0101, 0111, 0001, 1100, 1001 是不允许出现的, 因为这些状态是不安全状态。
f( farmer) , w(wolf) , g(goat) , c( cabbage) 分别表示农夫自己、农夫携带狼、农夫携带羊、农夫携带菜过河。
现在的问题转化为: 找一条合法路径( 相邻状态之间的转移合法) , 从开始状态到某个结束状态, 途中不经过不安全状态。
2.2算法的设计求农夫、狼、白菜和羊的当前状态的函数为每一种状态做测试, 状态安全则返回0, 否则返回1。
安全性判断函数, 若状态安全则返回0int farmer(int location) //判断农夫位置对0做与运算,还是原来的数字,用来判断位置{return 0 != (location & 0x08);}int wolf(int location) //判断狼位置{return 0 != (location & 0x04);}int cabbage(int location) //判断白菜位置{return 0 != (location & 0x02);}int goat(int location) //判断羊的位置{return 0 !=(location & 0x01);}int safe(int location) // 若状态安全则返回 true{if ((goat(location) == cabbage(location)) && (goat(location) != farmer(location)) )return 0;if ((goat(location) == wolf(location)) && (goat(location) != farmer(location)))return 0;return 1; //其他状态是安全的借助于位向量和按位运算符, 很容易描述过河动作, 这种问题表示的设计使得程序的实现比较容易。
农夫过河类似的数学题
农夫过河类似的数学题
1. 船夫过河问题:一个船夫要带着他的狼、羊和白菜过河,每次只能带一种东西过河,如果狼和羊单独留在一起,狼会吃掉羊,如果羊和白菜单独留在一起,羊会吃掉白菜,问船夫如何才能将狼、羊和白菜都安全地带过河?
2. 猴子过河问题:有两只猴子分别在河的两岸,它们要互相交换一个桃子,但是它们之间有一条河,猴子不会游泳,那么它们如何才能交换桃子呢?
3. 士兵过河问题:一个士兵和他的司令分别在河的两岸,他们要互相交换一把剑,但是士兵不能单独留下司令,否则司令会杀死士兵,他们如何才能交换剑呢?
4. 农民过河问题:有四个农民和一条狗在河的左岸,他们要到河的右岸去,但是只有一条船,这条船只能容纳两个人或者一条狗,而这条狗不能和任何一个农民单独留在一起,否则农民会偷走狗,那么他们如何才能全部安全地到达河的右岸呢?。
农夫过河问题的算法与实现汇总
农夫过河问题的算法与实现院(系)名称专业班级学号学生姓名指导教师年月日目录引言 (1)一.问题的描述 (2)二.需求分析 (3)三.概要设计 (4)3.1数据结构的设计 (4)3.2算法的设计 (5)3.3抽象数据类型的设计 (5)四.详细设计 (6)4.1算法的主要思想 (6)4.2主要功能函数设计 (7)4.3算法的实现 (7)五.代码实现 (10)六.测试与运行 (18)6.1测试工具 (18)6.2运行结果 (18)七.总结与体会 (19)八.参考文献 (20)农夫过河问题的算法与实现引言所谓农夫过河问题是指农夫带一只狼、一只羊和一棵白菜在河南岸, 需要安全运到北岸。
一条小船只能容下他和一件物品, 只有农夫能撑船。
问农夫怎么能安全过河, 当然狼吃羊, 羊吃白菜, 农夫不能将这两种或三种物品单独放在河的一侧, 因为没有农夫的照看, 狼就要吃羊, 而羊可能要吃白菜? 这类问题的实质是系统的状态问题, 要寻求的是从初始状态经一系列的安全状态到达系统的终止状态的一条路径.一.问题的描述任何的实际问题,都可以抽象成固定的数学模型,然后再根据模型解决问题。
这样就可以不考虑繁琐的实际过程,从而简化问题。
在我们的问题中,过河与没过河是两种不同的状态。
农夫、狼、羊和菜,分别处于这两种状态。
而,如果把他们看成一个系统,则农夫、狼、羊和菜的不同状态组合成系统的2的4次方种,即16种状态。
但在系统的16种状态中,有些不满足题给条件,应给予剔除。
剔除的判断条件:羊跟狼、菜状态相同,且不同于农夫的状态。
当我们挑选好一系列系统的合法状态后,我们的问题就清晰了很多。
我们不妨设,没过河状态为0,过河为1。
我们的问题就抽象为,系统从初始状态(0000),经过有限的合法状态,到达最终状态(1111)的过程。
系统不同的合法状态之间,可能,有的有路,有的则不能到达。
具体的判断条件是,农夫可以与一件物品同时边,或自己单独变。
根据这一个条件,我们可以抽象出一个图来:系统的每一种状态视为一个节点,满足条件的节点间有一条路。
农夫过河问题
课程设计题目:农夫过河一.问题描述一个农夫带着一只狼、一只羊和一箩白菜,身处河的南岸。
他要把这些东西全部运到北岸。
他面前只有一条小船,船只能容下他和一件物品,另外只有农夫才能撑船。
过河有以下规则:(1) 农夫一次最多能带一样东西(或者是狼、或者是羊、或者是白菜)过河;(2) 当农夫不在场是狼会吃羊;(3) 当农夫不在场是羊会吃掉白菜。
现在要求为农夫想一个方案,能将3 样东西顺利地带过河。
从出事状态开始,农夫将羊带过河,然后农夫将羊待会来也是符合规则的,然后农夫将羊带过河仍然是符合规则的,但是如此这般往返,搜索过程便进入了死循环,因此,在这里,采用改进的搜索算法进行搜索。
二.基本要求(1) 为农夫过河问题抽象数据类型,体会数据模型在问题求解中的重要性;(2) 要求利用数据结构的方法以及C + +的编程思想来完成问题的综合设计;(3) 在问题的设计中,使用深度优先遍历搜索方式,避免死循环状态;(4) 设计一个算法求解农夫过河问题,并输出过河方案;(5) 分析算法的时间复杂度。
三.概要设计(1) 数据结构的设计typedef struct // 图的顶点{int farmer; // 农夫int wolf; // 狼int sheep; // 羊int veget; // 白菜}Vertex;设计Vertex 结构体的目的是为了存储农夫、狼、羊、白菜的信息,因为在遍历图的时候,他们的位置信息会发生变化,例如1111 说明他们都在河的北岸,而0000 说明他们都在河的南岸。
t ypedef struct{int vertexNum; // 图的当前顶点数Vertex vertex[VertexNum]; // 顶点向量(代表顶点)bool Edge[VertexNum][VertexNum]; // 邻接矩阵 . 用于存储图中的边,其矩阵元素个数取决于顶点个数,与边数无关}AdjGraph; // 定义图的邻接矩阵存储结构存储图的方法是用邻接矩阵,所以设计一个简单的AdjGraph 结构体是为了储图的顶点数与边数,农夫过河问题我采用的是图的深度优先遍历思想。
农夫过河问题算法邻接矩阵
农夫过河问题是一个经典的人工智能搜索问题,其中一个农夫需要将一只狼、一只羊和一颗白菜从一侧河岸运到另一侧。
在此过程中,农夫要保证狼不吃羊,羊不吃白菜。
邻接矩阵是一种表示图形数据的方法,它可以用矩阵的形式来表示节点之间的关系。
对于农夫过河问题,我们可以将其表示为一个有向图,其中每个节点表示一个状态,每个边表示两个状态之间的转换。
设农夫、狼、羊、白菜分别用F、W、S、C表示,每个状态都包含四个元素的二进制数,表示它们是否在左侧河岸。
例如,初始状态可以表示为(1,1,1,1),即FWSC都在左侧河岸。
下面是农夫过河问题的邻接矩阵:(1,1,1 ,1) (0,1,0,1)(0,0,0,1)(0,0,0,0)(1,0,1,0)(1,0,0,0)(1,1,0,1)(1,1,0,0)(1,1,1,1)0 0 0 0 0 0 0 0(0,1,0,1)0 0 1 0 0 0 1 0(0,0,0,1)0 0 0 0 0 0 0 1(0,0,0,0)0 0 0 0 1 0 0 0(1,0,1,0)0 0 1 0 0 1 0 0(1,0,0,0)0 0 0 1 0 0 0 0(1,1,0,1)0 0 0 0 0 0 0 0(1,1,0,0)0 0 0 0 0 0 0 0在该邻接矩阵中,每个元素(i, j)表示从状态i到状态j是否存在一条转移边。
例如,第二行第三列的元素为1,表示从状态(1,1,1,1)到状态(0,0,0,1)存在一条转移边。
注意到邻接矩阵是对称的,即(i, j)和(j, i)的值相同,因为我们可以反过来将船驶回去。
同时,由于初始状态和最终状态都在左侧河岸,因此它们之间的转移边值都为0。
C# 关于农夫过河的问题
课程设计第五题1.题目探讨农夫过河的问题2.设计思路(1)农夫由A到B的情况(1)带走一个动物后,另外的两个物是不是可以相吃(2)会不会把刚带回来的又带到对岸去(3)最后一次带的情况比较的特殊,判断最后一次走后是不是所有的都不在了对岸(2)农夫从B到A的情况(1)对岸是不是只有一个物体(2)对岸是不是有两个物体,若是有两个物体的话他们的处理方式不一样,1。
两个物体会相克的,则把羊带走,2如果不是的话那么农夫就空着手回去。
3.应该注意的问题(1)。
在最后的一次带走羊的时候,应该加如一个新的判段(2)。
以及在带回羊的时候,我们要对其进行一个标记,不然的话我们就很容易进行死循环里面(3)以及每一次从A带东西去B的时候,我们都要对他的位置进行标记下4.咸受在对一个问题分析的时候,我们应该先把每一种情况给分析出来,要把问题分析得分面一些,最好是不要落下任何的一种情况,这样我们就可以对每一种情况编写相应的代码了。
5.源代码static int sum(int[] a){int sum = 0;for (int i = 0; i < a.Length; i++){sum = sum + a[i];}return sum;}static void Main(string[] args){string[] wu = new string[4] { "农夫", "狼", "羊", "白菜" };int[] a = new int[4] { 3, 1, -1, 1 };int[] b = new int[4] { 0, 0, 0, 0 };Console.WriteLine(" 关于农夫过河的问题");int weizhi = 0;do{if (a[0] == 3){for (int i = 1; i < 4; i++){if (a[i] == 0 || weizhi == i ){continue;}b[i] = a[i];a[i] = 0;if (a[1]==0&&a[3]==0&&a[2]==0){Console.WriteLine(" 农夫带羊到B岸\n");}else if (sum(a) == 3){a[i] = b[i];b[i] = 0;}else{if (a[i] == 0){Console.WriteLine(" 农夫带{0}去B岸:\n", wu[i]);weizhi = i;break;}}}a[0] = 0;b[0] = 3;}else{if (sum(b) - 3 == 1 || sum(b) - 3 == -1){a[0] = b[0];b[0] = 0;Console.WriteLine(" 农空手回去A:\n");}else{if (sum(b) == 3){//带Y ANG回去a[0] = b[0];b[0] = 0;a[2] = b[2];b[2] = 0;Console.WriteLine(" 农夫带羊回A岸:\n");weizhi = 2;}else{a[0] = b[0];b[0] = 0;Console.WriteLine(" 农夫空手回去A:\n");//农回去}}}} while (sum(b) != 4);Console.Read();}1。
农夫过河问题状态图及程序
农夫过河问题状态图及程序一、问题需求分析一个农夫带着一只狼、一只羊和一棵白菜,身处河的南岸。
他要把这些东西全部运到北岸。
问题是他面前只有一条小船,船小到只能容下他和一件物品,另外只有农夫能撑船。
另外,因为狼能吃羊,而羊爱吃白菜,所以农夫不能留下羊和白菜或者狼和羊单独在河的一边,自己离开。
请问农夫该采取什么方案才能将所有的东西运过河呢?二、算法选择求解这个问题的最简单的方法是一步一步进行试探,每一步都搜索所有可能的选择,对前一步合适的选择再考虑下一步的各种方案。
用计算机实现上述求解的搜索过程可以采用两种不同的策略:一种是广度优先(breadth_first) 搜索,另一种是深度优先(depth_first) 。
广度优先:u 广度优先的含义就是在搜索过程中总是首先搜索下面一步的所有可能状态,然后再进一步考虑更后面的各种情况。
u 要实现广度优先搜索,一般都采用队列作为辅助结构。
把下一步所有可能达到的状态都列举出来,放在这个队列中,然后顺序取出来分别进行处理,处理过程中把再下一步的状态放在队列里……。
u 由于队列的操作遵循先进先出的原则,在这个处理过程中,只有在前一步的所有情况都处理完后,才能开始后面一步各情况的处理。
三、算法的精化要模拟农夫过河问题,首先需要选择一个对问题中每个角色的位置进行描述的方法。
一个很方便的办法是用四位二进制数顺序分别表示农夫、狼、白菜和羊的位置。
例如用0表示农夫或者某东西在河的南岸,1表示在河的北岸。
因此整数5(其二进制表示为0101) 表示农夫和白菜在河的南岸,而狼和羊在北岸。
四、算法的实现完成了上面的准备工作,现在的问题变成:从初始状态二进制0000(全部在河的南岸) 出发,寻找一种全部由安全状态构成的状态序列,它以二进制1111(全部到达河的北岸) 为最终目标,并且在序列中的每一个状态都可以从前一状态通过农夫(可以带一样东西)划船过河的动作到达。
为避免不必要的瞎费功夫,要求在序列中不应该出现重复的状态。
十个有趣的智力小测试
十个有趣的智力小测试一、智力小测试1:农夫过河有一个农夫带着一只狼、一只羊和一棵白菜要过河。
但是船很小,每次只能带一样东西过河。
如果农夫不在,狼会吃羊,羊会吃白菜。
那农夫要怎么才能把狼、羊和白菜都安全运到河对岸呢?这题主要是考验逻辑思维能力啦。
其实答案是这样的,农夫先把羊运到河对岸,然后农夫自己返回。
接着农夫把狼运到河对岸,但是这时候不能把羊单独留下,所以要把羊再带回来。
然后把白菜运到河对岸,农夫再自己返回。
最后把羊运到河对岸就好啦。
这就像我们生活中安排事情的先后顺序一样,要考虑到不同事物之间的相互关系呢。
二、智力小测试2:水壶装水有两个水壶,一个能装5升水,一个能装3升水,旁边有个无限量的水源,怎么用这两个水壶量出4升水呢?这个题很有趣哦。
我们可以先把3升的水壶装满水,倒入5升的水壶中。
然后再把3升的水壶装满水,继续倒入5升的水壶直到5升的水壶满了,这时候3升的水壶里还剩1升水。
把5升水壶里的水倒掉,把3升水壶里剩下的1升水倒入5升水壶。
再把3升水壶装满水倒入5升水壶,这样5升水壶里就有4升水啦。
这就好比我们在做资源分配的时候,要巧妙地利用有限的资源来达到目的呢。
三、智力小测试3:称乒乓球有12个乒乓球,其中有一个重量和其他的不一样(不知道是轻是重),用一个没有砝码的天平称三次,找出这个不一样的乒乓球。
这题有点烧脑呢。
我们把12个球平均分成三组,每组4个。
第一次称其中两组,如果天平平衡,那不一样的球就在没称的那组里。
如果不平衡,就记住哪边重哪边轻。
然后从有问题的那组里拿两个球和正常组的两个球称第二次,如果平衡,不一样的球就在剩下的两个里,如果不平衡,就根据之前记住的轻重情况判断不一样的球在这两个里还是另外两个里。
第三次称就可以确定不一样的球了。
这就像我们在排查问题的时候,要逐步缩小范围呢。
四、智力小测试4:青蛙跳井一口井深10米,一只青蛙在井底,它每次能跳3米,但每次跳完后会滑下2米,这只青蛙需要跳几次才能跳出井口呢?这青蛙有点调皮呢。
农夫过河问题算法设计与实现
农夫过河问题算法设计与实现咱来唠唠农夫过河这个有趣的问题。
一、问题描述农夫要带着一只狼、一只羊和一棵白菜过河。
但是呢,船很小,每次农夫只能带一样东西过河。
而且啊,如果农夫不在,狼会吃羊,羊会吃白菜,这可就麻烦大了。
二、算法设计思路1. 初始状态- 河这边有农夫、狼、羊和白菜,河对岸啥都没有。
2. 第一步很关键- 农夫肯定不能先带狼或者白菜,为啥呢?因为如果先带狼过去,把羊和白菜留在这边,羊就会吃白菜;要是先带白菜过去,狼就会吃羊。
所以农夫得先带羊过河。
- 然后农夫自己返回。
这时候河这边有狼和白菜,河对岸有羊。
3. 第二步- 农夫再带狼或者白菜过河。
假设农夫带狼过河吧。
- 但是农夫不能把羊单独留在对岸啊,所以农夫得把羊再带回来。
这时候河这边有羊和白菜,河对岸有狼。
4. 第三步- 农夫把羊放下,带白菜过河。
- 然后农夫自己返回。
这时候河这边有羊,河对岸有狼和白菜。
5. 最后一步- 农夫再带羊过河,这样就都安全地到对岸啦。
三、算法实现(用简单的文字描述过程)1. 开始:- 农夫在起始岸,物品(狼、羊、白菜)也在起始岸,对岸为空。
2. 第一次过河:- 农夫带羊过河,此时起始岸有狼和白菜,对岸有羊和农夫。
- 农夫独自返回起始岸,此时起始岸有农夫、狼、白菜,对岸有羊。
3. 第二次过河:- 农夫带狼过河,此时起始岸有白菜,对岸有狼、羊和农夫。
- 农夫带羊返回起始岸,此时起始岸有农夫、羊、白菜,对岸有狼。
4. 第三次过河:- 农夫带白菜过河,此时起始岸有羊,对岸有狼、白菜和农夫。
- 农夫独自返回起始岸,此时起始岸有农夫、羊,对岸有狼、白菜。
5. 第四次过河:- 农夫带羊过河,此时起始岸为空,对岸有农夫、狼、羊、白菜,大功告成!你看,这样一步一步的,就像走迷宫一样,按照规则走,就能让农夫带着他的东西安全过河啦。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
课程设计题目:农夫过河一.问题描述一个农夫带着一只狼、一只羊和一箩白菜,身处河的南岸。
他要把这些东西全部运到北岸。
他面前只有一条小船,船只能容下他和一件物品,另外只有农夫才能撑船。
过河有以下规则:(1)农夫一次最多能带一样东西(或者是狼、或者是羊、或者是白菜)过河;(2)当农夫不在场是狼会吃羊;(3)当农夫不在场是羊会吃掉白菜。
现在要求为农夫想一个方案,能将3样东西顺利地带过河。
从出事状态开始,农夫将羊带过河,然后农夫将羊待会来也是符合规则的,然后农夫将羊带过河仍然是符合规则的,但是如此这般往返,搜索过程便进入了死循环,因此,在这里,采用改进的搜索算法进行搜索。
二.基本要求(1)为农夫过河问题抽象数据类型,体会数据模型在问题求解中的重要性;(2)要求利用数据结构的方法以及C++的编程思想来完成问题的综合设计;(3)在问题的设计中,使用深度优先遍历搜索方式,避免死循环状态;(4)设计一个算法求解农夫过河问题,并输出过河方案;(5)分析算法的时间复杂度。
三.概要设计(1)数据结构的设计typedef struct // 图的顶点{int farmer; // 农夫int wolf; // 狼int sheep; // 羊int veget; // 白菜}Vertex;设计Vertex结构体的目的是为了存储农夫、狼、羊、白菜的信息,因为在遍历图的时候,他们的位置信息会发生变化,例如1111说明他们都在河的北岸,而0000说明他们都在河的南岸。
t ypedef struct{int vertexNum; // 图的当前顶点数Vertex vertex[VertexNum]; // 顶点向量(代表顶点)bool Edge[VertexNum][VertexNum]; // 邻接矩阵. 用于存储图中的边,其矩阵元素个数取决于顶点个数,与边数无关}AdjGraph; // 定义图的邻接矩阵存储结构存储图的方法是用邻接矩阵,所以设计一个简单的AdjGraph结构体是为了储图的顶点数与边数,农夫过河问题我采用的是图的深度优先遍历思想。
(2)算法的设计深度优先遍历基本设计思想:设x是当前被访问顶点,在对x做过访问标记后,选择一条从x出发的未检测过的1/12边(x,y)。
若发现顶点y已访问过,则重新选择另一条从x出发的未检测过的边,否则沿边(x,y)到达未曾访问过的y,对y访问并将其标记为已访问过;然后从y开始搜索,直到搜索完从y出发的所有路径,即访问完所有从y出发可达的顶点之后,才回溯到顶点x,并且再选择一条从x出发的未检测过的边。
上述过程直至从x出发的所有边都已检测过为止。
此时,若x不是源点,则回溯到在x之前被访问过的顶点;否则图中所有和源点有路径相通的顶点(即从源点可达的所有顶点)都已被访问过,若图G是连通图,则遍历过程结束,否则继续选择一个尚未被访问的顶点作为新源点,进行新的搜索过程。
程序中的深度优先遍历算法如下:void dfsPath(AdjGraph *graph, int start, int end) // 深度优先搜索从u到v的简单路径//DFS--Depth First Search{int i = 0;visited[start] = true; //标记已访问过的顶点if (start == end){return ;}for (i = 0; i < graph->vertexNum; i++){if (graph->Edge[start][i] && !visited[i]){retPath[start] = i;dfsPath(graph, i, end);}}}(3)抽象数据类型设计int locate(AdjGraph *graph, int farmer, int wolf, int sheep, int veget) // 查找顶点(F,W,S,V)在顶点向量中的位置bool isSafe(int farmer, int wolf, int sheep, int veget) // 判断目前的(F,W,S,V)是否安全bool isConnect(AdjGraph *graph, int i, int j) // 判断状态i与状态j之间是否可转换void printPath(AdjGraph *graph, int start, int end) // 输出从u到v的简单路径,即顶点序列中不重复出现的路径void dfsPath(AdjGraph *graph, int start, int end) // 深度优先搜索从u到v的简单路径 //DFS--Depth First Search四.详细设计1.问题遵循的原则(1)图:顶点和连线的集合,G=(V,E),其中V是图中顶点的有穷非空集合,E是两个顶点的关系的集合,即图中连线的集合。
若E中顶点对<v,u>是有序的,则为有向图,否则为无向图。
(2)网:带权值的图称为网(3)邻接矩阵:表示顶点之间连接关系的矩阵。
2.算法描述(1)深度优先遍历的递归算法:typedef enum{FALSE,TRUE}Boolean;//FALSE为0,TRUE为Boolean visited[MaxVertexNum]; //访问标志向量是全局量void DFSTraverse(ALGraph *G) //深度优先遍历以邻接表表示的图G,而以邻接矩阵表示G时,算法完全与此相同 int i;{for(i=0;i<G->n;i++)visited[i]=FALSE; //标志向量初始for(i=0;i<G->n;i++)if(!visited[i]) //vi未访问过DFS(G,i); //以vi为源点开始DFS搜索}//DFSTraverse(2)邻接矩阵表示的深度优先遍历算法:void DFSM(MGraph *G,int i) //以vi为出发点对邻接矩阵表示的图G进行DFS搜索,设邻接矩阵是0,l矩阵 int j;{printf("visit vertex:%c",G->vexs[i]);//访问顶点vivisited[i]=TRUE;for(j=0;j<G->n;j++) //依次搜索vi的邻接点if(G->edges[i][j]==1&&!visited[j])DFSM(G,j)//(vi,vj)∈E,且vj未访问过,故vj为新出发点}//DFS五.运行与测试1.程序清单#include<iostream>using namespace std;#define VertexNum 100 //最大顶点数typedef struct // 图的顶点{int farmer; // 农夫int wolf; // 狼int sheep; // 羊int veget; // 白菜}Vertex;typedef struct{int vertexNum; // 图的当前顶点数Vertex vertex[VertexNum]; // 顶点向量(代表顶点)bool Edge[VertexNum][VertexNum]; // 邻接矩阵. 用于存储图中的边,其矩阵元素个数取决于顶点个数,与边数无关}AdjGraph; // 定义图的邻接矩阵存储结构bool visited[VertexNum] = {false}; // 对已访问的顶点进行标记(图的遍历)int retPath[VertexNum] = {-1}; // 保存DFS搜索到的路径,即与某顶点到下一顶点的路径// 查找顶点(F,W,S,V)在顶点向量中的位置int locate(AdjGraph *graph, int farmer, int wolf, int sheep, int veget){// 从0开始查找for (int i = 0; i < graph->vertexNum; i++){if ( graph->vertex[i].farmer == farmer && graph->vertex[i].wolf == wolf && graph->vertex[i].sheep == sheep && graph->vertex[i].veget == veget ){return i; //返回当前位置}}return -1; //没有找到此顶点}// 判断目前的(F,W,S,V)是否安全bool isSafe(int farmer, int wolf, int sheep, int veget){//当农夫与羊不在一起时,狼与羊或羊与白菜在一起是不安全的if ( farmer != sheep && (wolf == sheep || sheep == veget) ){return false;}else{return true; // 安全返回true}}// 判断状态i与状态j之间是否可转换bool isConnect(AdjGraph *graph, int i, int j){int k = 0;if (graph->vertex[i].wolf != graph->vertex[j].wolf){k++;}if (graph->vertex[i].sheep != graph->vertex[j].sheep){k++;}if (graph->vertex[i].veget != graph->vertex[j].veget){k++;}// 以上三个条件不同时满足两个且农夫状态改变时,返回真, 也即农夫每次只能带一件东西过桥if (graph->vertex[i].farmer != graph->vertex[j].farmer && k <= 1){return true;}else{return false;}}// 创建连接图void CreateG(AdjGraph *graph){int i = 0;int j = 0;// 生成所有安全的图的顶点for (int farmer = 0; farmer <= 1; farmer++){for (int wolf = 0; wolf <= 1; wolf++){for (int sheep = 0; sheep <= 1; sheep++){for (int veget = 0; veget <= 1; veget++){if (isSafe(farmer, wolf, sheep, veget)){graph->vertex[i].farmer = farmer;graph->vertex[i].wolf = wolf;graph->vertex[i].sheep = sheep;graph->vertex[i].veget = veget;i++;}}}}}// 邻接矩阵初始化即建立邻接矩阵graph->vertexNum = i;for (i = 0; i < graph->vertexNum; i++){for (j = 0; j < graph->vertexNum; j++){// 状态i与状态j之间可转化,初始化为1,否则为0if (isConnect(graph, i, j)){graph->Edge[i][j] = graph->Edge[j][i] = true;}else{graph->Edge[i][j] = graph->Edge[j][i] = false;}}}return;}// 判断在河的那一边char* judgement(int state){if (state==0)return("南岸");elsereturn("北岸");// return ( (0 == state) ? "南岸" : "北岸" );}// 输出从u到v的简单路径,即顶点序列中不重复出现的路径void printPath(AdjGraph *graph, int start, int end){int i = start;cout << "farmer" << ", wolf" << ", sheep" << ", veget" << endl;while (i != end){cout << "(" << judgement(graph->vertex[i].farmer) << ", " << judgement(graph->vertex[i].wolf)<< ", " << judgement(graph->vertex[i].sheep) << ", " << judgement(graph->vertex[i].veget) << ")";cout << endl;i = retPath[i];}cout << "(" << judgement(graph->vertex[i].farmer) << ", " << judgement(graph->vertex[i].wolf)<< ", " << judgement(graph->vertex[i].sheep) << ", " << judgement(graph->vertex[i].veget) << ")";cout << endl;}// 深度优先搜索从u到v的简单路径 //DFS--Depth First Searchvoid dfsPath(AdjGraph *graph, int start, int end){int i = 0;visited[start] = true; //标记已访问过的顶点if (start == end){return ;}for (i = 0; i < graph->vertexNum; i++){if (graph->Edge[start][i] && !visited[i]){retPath[start] = i;dfsPath(graph, i, end);}}}int main(){AdjGraph graph;CreateG(&graph);int start = locate(&graph, 0, 0, 0, 0);int end = locate(&graph, 1, 1, 1, 1);dfsPath(&graph, start, end);if (visited[end])// 有结果{printPath(&graph, start, end);return 0;}return -1;}2.运行结果六.总结与心得这次的编程让我受益良多,从一窍不通到现在自己完成课设题目,觉得自己进步了一点。