数据结构课程设计报告(农夫过河)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
目录
引言 (2)
1 问题描述 (2)
基本要求 (2)
2.1为农夫过河问题抽象数据模型体会数据模型在问题求解中的重要性; (2)
2.2设计一个算法求解农夫过河问题,并输出过河方案; (2)
3 概要设计 (2)
3.1数据结构的设计。
(2)
3.1.1农夫过河问题的模型化 (2)
3.1.2 算法的设计 (3)
4、运行与测试 (5)
5、总结与心得 (6)
附录 (6)
参考文献 (12)
引言
所谓农夫过河问题是指农夫带一只狼、一只羊和一棵白菜在河南岸, 需要安全运到北岸。
一条小船只能容下他和一件物品, 只有农夫能撑船。
问农夫怎么能安全过河, 当然狼吃羊, 羊吃白菜, 农夫不能将这两种或三种物品单独放在河的一侧, 因为没有农
夫的照看, 狼就要吃羊, 而羊可能要吃白菜? 这类问题的实质是系统的状态问题, 要
寻求的是从初始状态经一系列的安全状态到达系统的终止状态的一条路径。
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 所示。
其中双向的箭头表示状态可逆, 即农夫可以带着某种东西过去, 也可以带着该东西回来。
箭头上的字母表示农夫所携带的东西: f( farmer) , w(wolf) , g(goat) , c( cabbage) 分别表示农夫自己、农夫携带狼、农夫携带羊、农夫携带菜过河。
现在的问题转化为: 找一条合法路径( 相邻状态之间的转移合法) , 从开始状态到某个结束状态, 途中不经过不安全状态。
3.1.2 算法的设计
求农夫、狼、白菜和羊的当前状态的函数为每一种状态做测试, 状态安全则返回0, 否则返回1。
安全性判断函数, 若状态安全则返回0
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; //其他状态是安全的
借助于位向量和按位运算符, 很容易描述过河动作, 这种问题表示的设计使得程序的实现比较容易。
算法的基本思想: 利用队列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); //初始状态为0
for (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 20
typedef 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;
else
paqu->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;
else
paqu->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); //初始状态为0
for (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;
}
}
else
cout<<"No solution."<<endl;
}
int main() /*主函数*/
{
farmerProblem();
return 0;
}
参考文献
1.王红梅、王涛、胡明编著。
数据结构。
北京:清华大学出版社出版,2011.6
2.王红梅编著。
算法设计与分析。
北京:清华大学出版社,2006。