目录引言 (2)1 问题描述 (3)基本要求 (3)2.1为农夫过河问题抽象数据模型体会数据模型在问题求解中的重要性; (3)2.2设计一个算法求解农夫过河问题,并输出过河方案; (3)3 概要设计 (3)3.1数据结构的设计。
(3)3.1.1农夫过河问题的模型化 (3)3.1.2 算法的设计 (4)4、运行与测试 (6)5、总结与心得 (7)附录 (7)参考文献 (13)引言所谓农夫过河问题是指农夫带一只狼、一只羊和一棵白菜在河南岸, 需要安全运到北岸。
一条小船只能容下他和一件物品, 只有农夫能撑船。
问农夫怎么能安全过河, 当然狼吃羊, 羊吃白菜, 农夫不能将这两种或三种物品单独放在河的一侧, 因为没有农夫的照看, 狼就要吃羊, 而羊可能要吃白菜? 这类问题的实质是系统的状态问题, 要寻求的是从初始状态经一系列的安全状态到达系统的终止状态的一条路径。
1 问题描述一个农夫带一只狼、一棵白菜和一只羊要从一条河的南岸过到北岸,农夫每次只能带一样东西过河,但是任意时刻如果农夫不在场时,狼要吃羊、羊要吃白菜,请为农夫设计过河方案。
基本要求2.1为农夫过河问题抽象数据模型体会数据模型在问题求解中的重要性;2.2设计一个算法求解农夫过河问题,并输出过河方案;3 概要设计3.1 数据结构的设计。
3.1.1农夫过河问题的模型化分析这类问题会发现以下特征:有一组状态( 如农夫和羊在南, 狼和白菜在北) ; 从一个状态可合法地转到另外几个状态( 如农夫自己过河或农夫带着羊过河) ; 有些状态不安全( 如农夫在北, 其他东西在南) ; 有一个初始状态( 都在南) ; 结束状态集( 这里只有一个, 都在北) 。
问题表示: 需要表示问题中的状态, 农夫等位于南P北( 每个有两种可能) 。
可以采用位向量, 4 个二进制位的0P1 情况表示状态, 显而易见, 共24= 16种可能状态。
从高位到低位分别表示农夫、狼、白菜和羊。
0000( 整数0) 表示都在南岸, 目标状态1111( 即整数15) 表示都到了北岸。
有些状态0011,0101, 0111, 0001, 1100, 1001 是不允许出现的, 因为这些状态是不安全状态。
根据上述条件可以画出系统的状态图如图1 所示。
图1 系统状态转换图其中双向的箭头表示状态可逆, 即农夫可以带着某种东西过去, 也可以带着该东西回来。
箭头上的字母表示农夫所携带的东西:f( farmer) , w(wolf) , g(goat) , c( cabbage) 分别表示农夫自己、农夫携带狼、农夫携带羊、农夫携带菜过河。
现在的问题转化为: 找一条合法路径( 相邻状态之间的转移合法) , 从开始状态到某个结束状态, 途中不经过不安全状态。
3.1.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; //其他状态是安全的借助于位向量和按位运算符, 很容易描述过河动作, 这种问题表示的设计使得程序的实现比较容易。
算法的基本思想: 利用队列moveTo 记录可到的尚未向前探试的状态, 数组元素route [ i] 记录状态i的路径[ 前一状态] , - 1 表示尚未访问。
则算法的高级抽象可描速为:{初始状态出发点入队列;所有其他点状态标记为未访问;while ( ! isEmptyQueue seq ( moveTo) &&( route[ 15] == - 1) ){从moveTo 取出一个状态;试探所有由这个状态出发可能到达的状态;if( 能到达的状态安全且未访问过){记录到它的路径;压入队列;}}}精化上速算法得到问题的具体算法如下:void farmerProblem( ){int movers, i, location, newlocation;int route[16]; //记录已考虑的状态路径int print[MAXNUM];PSeqQueue moveTo;moveTo = createEmptyQueue_seq( );//新的队列判断路径enQueue_seq(moveTo, 0x00); //初始状态为0for (i = 0; i < 16; i++)route[i] = -1; //-1表示没有记录过路径route[0]=0;while (!isEmptyQueue_seq(moveTo)&&(route[15]== -1))//队列不为空,路径未满时循环{location = frontQueue_seq(moveTo); //从队头出队,location表示位置,0为北岸,1为南岸deQueue_seq(moveTo);//已出队的删除for (movers = 1; movers <= 8; movers<<= 1) //向左移位,movers分别0001,0010,0100,1000,也就是依次判断过河的可行性{if ((0 != (location & 0x08)) == (0 != (location & movers)))//判断农夫和要移动的物品是否在同岸{newlocation = location^(0x08|movers);//过岸if (safe(newlocation) && (route[newlocation] == -1))//判断是否安全,以及路径是否可用{route[newlocation] = location;enQueue_seq(moveTo, newlocation);//记录路径并入队,位置改变4、运行与测试5、总结与心得“纸上得来终觉浅,绝知此事要躬行。
”通过这两周的课程设计,使我对书上的的理论知识有了更深的理解,也使我对于团队合作有了新的认识,意识到团队的力量。
课程设计是一个必须经历的过程,是我们理解书本知识、熟悉所学专业的一次很好实践。
在这次设计过程中,体现出自己单独设计程序的能力以及综合运用知识的能力,体会了学以致用、突出自己劳动成果的喜悦心情,从中发现自己平时学习的不足和薄弱环节,从而加以弥补。
附录#include<iostream.h>#include<stdio.h>#define MAXNUM 20typedef struct //顺序队列类型定义{int f, r; //f表示头,r 表示尾int q[MAXNUM];//顺序队}SeqQueue ,*PSeqQueue;PSeqQueue createEmptyQueue_seq( ) //创建队列{PSeqQueue paqu = new SeqQueue;if (paqu == NULL)cout<<"Out of space!"<<endl;elsepaqu->f=paqu->r=0;return (paqu);}int isEmptyQueue_seq( PSeqQueue paqu ) //判断 paqu 所指是否是空队列{return paqu->f==paqu->r;}void enQueue_seq(PSeqQueue paqu,int x) //在队列中插入一元素 x{if ((paqu->r+1)%MAXNUM==paqu->f)cout<<"队列已满."<<endl;else{paqu->q[paqu->r]=x;paqu->r=(paqu->r+1)%MAXNUM;}}void deQueue_seq(PSeqQueue paqu) //删除队列头部元素{if( paqu->f==paqu->r)cout<<"队列为空"<<endl;elsepaqu->f=(paqu->f+1)%MAXNUM;}int frontQueue_seq( PSeqQueue paqu ) //对非空队列,求队列头部元素{return (paqu->q[paqu->f]);}int 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; //其他状态是安全的}void farmerProblem( ){int movers, i, location, newlocation;int route[16]; //记录已考虑的状态路径int print[MAXNUM];PSeqQueue moveTo;moveTo = createEmptyQueue_seq( );//新的队列判断路径enQueue_seq(moveTo, 0x00); //初始状态为0for (i = 0; i < 16; i++)route[i] = -1; //-1表示没有记录过路径route[0]=0;while (!isEmptyQueue_seq(moveTo)&&(route[15]== -1))//队列不为空,路径未满时循环{location = frontQueue_seq(moveTo); //从队头出队,location表示位置,0为北岸,1为南岸deQueue_seq(moveTo);//已出队的删除for (movers = 1; movers <= 8; movers<<= 1) //向左移位,movers分别0001,0010,0100,1000,也就是依次判断过河的可行性{if ((0 != (location & 0x08)) == (0 != (location & movers)))//判断农夫和要移动的物品是否在同岸{newlocation = location^(0x08|movers);//过岸if (safe(newlocation) && (route[newlocation] == -1))//判断是否安全,以及路径是否可用{route[newlocation] = location;enQueue_seq(moveTo, newlocation);//记录路径并入队,位置改变}}}}/* 打印出路径 */if(route[15] != -1){cout<<"过河步骤是 : "<<endl;i=0;for(location = 15; location >= 0; location = route[location]){print[i]=location;i++;if (location == 0)break;}int num=i-1;int temp[20][4];int j;for(i=num;i>=0;i--){for(j=3;j>=0;j--){temp[num-i][j]=print[i]%2;print[i]/=2;temp[0][j]=0;temp[num+1][j]=1;}}/* for(i=0;i<=num;i++){for(j=0;j<4;j++)cout<<temp[i][j]<<" ";cout<<endl;}*/for(i=1;i<=num;i++){cout<<"\t\t\tNO . "<<i<<"\t";if(i%2==1){if(temp[i][3]!=temp[i-1][3])cout<<"农夫带羊过南岸";if(temp[i][2]!=temp[i-1][2])cout<<"农夫带白菜过南岸";if(temp[i][1]!=temp[i-1][1])cout<<"农夫带狼过南岸";if(temp[i][3]==temp[i-1][3]&&temp[i][2]==temp[i-1][2]&&temp[i][1]==temp[i-1 ][1])cout<<"农夫自己过南岸";}else if(i%2==0){if(temp[i][3]!=temp[i-1][3])cout<<"农夫带羊回北岸";if(temp[i][2]!=temp[i-1][2])cout<<"农夫带白菜回北岸";if(temp[i][1]!=temp[i-1][1])cout<<"农夫带狼回北岸";if(temp[i][3]==temp[i-1][3]&&temp[i][2]==temp[i-1][2]&&temp[i][1]==temp[i-1 ][1])cout<<"农夫自己回北岸";}cout<<endl;}}elsecout<<"No solution."<<endl;}int main() /*主函数*/{farmerProblem();return 0;}参考文献1.王红梅、王涛、胡明编著。