当前位置:文档之家› 农夫过河问题

农夫过河问题

课程设计题目:农夫过河一.问题描述一个农夫带着一只狼、一只羊和一箩白菜,身处河的南岸。

他要把这些东西全部运到北岸。

他面前只有一条小船,船只能容下他和一件物品,另外只有农夫才能撑船。

过河有以下规则:(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.运行结果六.总结与心得这次的编程让我受益良多,从一窍不通到现在自己完成课设题目,觉得自己进步了一点。

相关主题