数据结构课程设计--农夫过河

来源:互联网 发布:java后台开发框架源码 编辑:程序博客网 时间:2024/06/08 16:03

农民过河

【摘要】

农民问题即一个农民带着一只狼、一只羊和一棵白菜,身处河的南岸,他需要把这些东西全部运到河的北岸。而他只有一条小船,且这只小船小到只能容下他和一件物品,另外只有农民能撑船。农民不能留下狼和羊自己离开,也不能留下白菜和羊自己离开,更不能留下狼,羊和白菜而独自离开,因为没有农民的照看,狼就要吃掉羊,而羊又要吃掉白菜。好在狼是是肉动物,它不吃白菜,问农民应该采取什么方案才能将所有的东西安全地从河的南岸运到北岸?这类农民问题是一个传统的数据结构问题, 利用基于队列的图的广度优先搜索求解农民过河问题是一个易于理解、切实可行的方案, 具有一定的推广价值。

   关键字:农民过河问题; 队列; 广度优先搜索

Farmer Pass Through The River

Abstract 

   

 "Farmer pass through the river "is a problem that one farmer should take a wolf, a cabbage and a goat to pass through the river from the south to the north safely. But there is only one small ship, which is too small to take all of them. What’s worse it is only the farmer who can drive the ship. Obviously, the wolf will eat the goat and the goat will eat the cabbage, so the farmer can’t leave with the wolf and the goat together, or with the goat and the cabbage together, even with the wolf, the goat and the cabbage all together. Luckily, the wolf is the animal that only eats the meat, so it will not eat the cabbage. How will the farmer take them to pass through the river safely? How to pass through the river safely with his belongings is at traditional data structure problem, to solve the farmer’s problem by the breadth - first searching graph, which can be done by the help with queue, is a easy to understand and feasible method, and it can be used widely.

Key words : farmer’s problem to pass through river ; Queue ; breadth - first search

 

一、 实验内容概述(设计任务与技术要求)

   农民过河问题是指农民需要带一只狼、一只羊和一棵白菜到河的南岸去,需要安全运到北岸。而一条小船只能容下他和一件物品,只有农民能撑船。问农民怎么能安全过河,问题中需要涉及到狼会吃羊,羊会吃白菜,所以农民不能将这两种或三种物品单独放在河的一侧,因为没有农民的照看,狼就要吃掉羊,而羊可能又要吃掉白菜。 这类问题的实质是系统的状态问题,要寻求的是从初始状态经一系列的安全状态到达系统的终止状态的一条路径。

根据实际情况,对此问题分析可以得到不同的特征:一是农民和羊在河的南岸,狼和白菜在河的北岸;二是从一个状态可转到另外一个状态,而不违反狼吃羊,羊吃草的规则(例如农民自己过河或者农民带着羊过河);三是有些状态是不安全的(例如农民一人在北岸,而其他的东西都在南岸);四是初始状态是农民,羊,狼,白菜都在河的南岸和结束状态是农民,羊,狼,白菜都在河的北岸。

实现农民过河问题,则需要找到一条合法的路径(相邻状态之间的转移合法),从开始状态到某个结束状态,路途中不能经过不安全的状态。

二、 实验目的概述(总体设计方案) 

a ) 利用图的有关知识来解决农民过河问题

b ) 根据图的广度(深度)优先搜索来实现实验要求

在实施此问题中,首先要为农民,狼,白菜和羊设置求状态的函数,若某一种物品在河的南岸,则返回0,若在河的的北岸,则返回1;其次是为每一种状态做测试,状态安全则返1,否则返回0

三、 解题思路的描述(数据结构和算法的设计):

 农民过河问题根据图求解的搜索过程可采用两种不同的策略:一种是图的深度优先遍历搜索,另外一种是广度优先遍历搜索。如果采用深度优先遍历搜索,则需要采用递归的方式来编写程序,而这种程序的系统的开销比较大,如果采用广度优先搜索,则可以借助队列的方式,这种方式开销较小。则我在此实验中,采取的是利用广度优先搜索来解决农民问题。

算法中要先各为农民,狼,白菜和羊设置参数,这可以用枚举类型为各种物品设置参数,其中羊goat=0001,白菜cabbage=0010,狼wolf=0100,农民farmer=1000,而且利用moveTo来记录可到的尚未向前测试的状态,数据元素route[i]来记录状态i的路径[前一状态],如果是-1,则表示尚未访问。

算法的总体思想为:

{

  初始化顺序表Route和安全队列moveTo

  初始安全状态00000)入队列moveTo,Route[0]=0

  While(IsEmpty(moveTo)&&(route[15]==-1))

   {

    OutQuere(moveTo,location);          //出队当前安全状态

    for(每个从location可以过渡到得状态newlocation)  //农民(附带同侧物品)移动

     if(newlocation安全且未访问过)

     OutQueue(moveTo,newlocation);

}

if(route[15]!=-1 //已经到达终点了,打印路径

 for(location=15;location>=0;location=route[location]

    {

      printf (“The location is : %dn",location);

      if(location==0) return 1;return 1;

}

else 

printf(“无解!\n");

}

在程序中涉及了很多的状态,共有16种,从高位到低位分别表示农民,狼,白菜和羊,如果是0000则表示农民,狼,白菜和羊都在南岸,1111表示都到达了北岸。其他的状态则分别表示:

0001表示羊在北岸                    0010表示白菜在北岸

0011表示羊和白菜在北岸              0100表示狼在北岸

0101表示狼和羊在北岸                0110表示狼和白菜北岸

0111表示狼,白菜和羊在北岸          1000表示农民和北岸

1001表示农民,羊在北岸              1010表示农民,白菜在北岸

1011表示农民,白菜和羊在北岸        1100表示农民,狼在北岸

1101表示农民,狼和羊在北岸          1110表示农民,狼和白菜在北岸

其中有些状态是不允许出现的,有001101010111000111001001,因为这些状态是不安全的,所以可以为以上的条件绘制体统的状态图:

3系统状态转换图

   其中箭头表示状态是可逆的,也就是说农民可以带着某些东西到河的北岸,也可以把东西带回河的南岸。箭头上的字母都代表这其中的物品,f(farmer)表示农民自己过河,w(wolf)表示农民带着狼过河,c(cabbage)表示农民带着白菜过河,g(goat)表示农民带着羊过河。

四、 源程序清单(源程序中应该附有必要的注释)

#include <stdio.h>

#include <malloc.h>

#define QUEUE_INIT_SIZE  100

#define QUEUE_INC  50

typedef int ElemType;

typedef struct

{

   ElemType *data;                  /* 动态分配存储空间*/

   int queuesize;

   int front;                        /*头指针,若队列不空,指向队列头元素*/

   int rear;                         /*尾指针,若队列不空,指向队列尾元素*/

} SqQueue;

bool InitQueue(SqQueue &Q)

{

 Q.data = (ElemType *)malloc(QUEUE_INIT_SIZE*sizeof(ElemType));

 if(!Q.data )return false;

 Q.front = 0;  Q.rear = 0; Q.queuesize = QUEUE_INIT_SIZE;

}

bool DestroyQueue(SqQueue &Q)

{

 if(Q.data){

  free(Q.data);Q.data = NULL;

 }

 return true;

}

void ClearQueue(SqQueue &Q)

{

 Q.front = 0;  Q.rear = 0;

}

bool QueueEmpty(SqQueue Q)

{

 return Q.front==Q.rear ;

}

bool GetHead(SqQueue Q,ElemType &e)

{

 if(Q.front==Q.rear) return false;

 e = Q.data[0];

 return true;

}

bool EnQueue(SqQueue &Q,ElemType e)

{

 if((Q.rear+1)%Q.queuesize==Q.front)

 {                                    /*队列满*/

  return false;

 }

 Q.data[Q.rear] = e;

        Q.rear = (Q.rear+1)%Q.queuesize;

 return true;

}

bool OutQueue(SqQueue &Q,ElemType &e)

{

 if(Q.front==Q.rear)

 return false;

 e=Q.data[Q.front];

 Q.front = (Q.front+1)%Q.queuesize;

 return true;

}

int safe(int location);

int farmerProblem( )

{

 int movers, i,location,  newlocation;

 int route[16];                               /*记录已考察的状态路径*/

 for(i=0;i<16;i++)

 route[i]=-1;

 SqQueue   moveTo;

 InitQueue(moveTo);

 EnQueue(moveTo,0x00);                      /*准备初值*/

 route[0]=0;

 while(!QueueEmpty(moveTo)&&(route[15]==-1))

 {

  OutQueue(moveTo,location);                  /*得到现在的状态*/

  for(movers=1;movers<=8;movers<<=1)

  {                                         /* 农民总是在移动,随农民移动的也只能是在农民同侧的东西 */

   if ((0!=(location & 0x08))==(0!=(location & movers)))

    {

    newlocation=location^(0x08|movers);

    if(safe(newlocation)&&(route[newlocation]==-1))

    {

     route[newlocation]=location;                /*记录路径*/

     EnQueue(moveTo,newlocation);             /*压入队列*/

    } 

   }

  }

 }

 if(route[15]!=-1)

 { 

printf("0001-goat0010-cabbage0100-wolf1000-farmer \n");                                       

  printf("The reverse path is : \n");

  for(location=15;location>=0;location=route[location])

  {

   printf("The location is : %d\n",location);

   if(location==0)

   return 1;

  }

 }

 else   printf("No solution.\n");

 return 0;

}

int main()

{

 farmerProblem();

}

int farmer(int location)                 /*为农民设置参数8*/

{

    return (0 != (location & 0x08));

}

int wolf(int location)                  /*为狼设置参数4*/

{

    return (0 != (location & 0x04));

}

int cabbage(int location)               /*为白菜设置参数2*/

{

    return (0 != (location & 0x02));

}

int goat(int location)                  /*为羊设置参数1*/

{

    return (0 !=(location & 0x01));

}

int safe(int location)               /* 为每种状态做测试,状态安全则返回1,否则返回0*/

{                                      /*安全性判断函数,安全则返回1*/

 if((goat(location) == cabbage(location))

  &&(goat(location)!=farmer(location)))

  return (0);                       /* 羊和白菜在河的一侧且人不在,则羊就要吃白菜*/

 if((goat(location)==wolf(location))

  && (goat(location)!= farmer(location)))

  return (0);                       /* 狼和羊在河的一侧且人不在,则狼就要吃羊*/

 return (1);                        /* 其他状态是安全的*/

}

五、 程序调试及测试结果

5程序运行结果

因为此上是农民路径的逆路径,所以可以得到从中得到农民过河的正路径为:

09111214615

将其转化为二进制就得到完整的农民过河的整个方案:

农民把羊带到北岸(0000 - 1001)

农民独自回到南岸(1001 - 0001)

农民把白菜带到北岸(0001 - 1011)

农民带着羊返回南岸(1011 - 0010)

农民把狼带到北岸(0010 - 1110)

农民独自返回南岸(1110 - 0110)

农民把羊带到北岸(0110 - 1111)

状态图的转化为:

   g   f

g

g f w

图 5运行结果路径

六、 结论

      此次大型作业题的设计,让我学到了好多,从拿到题目到完成整个实验,在这短短的一个

多月的时间里可以学到很多的东西,同时还可以巩固以前所学到得,更重要的事我还能学到书本没学到的知识。在这过程中,我学会了实际动手能力和独立思考的能力,虽然困难重重,但我还是通过查阅书籍和网络,还有身边同学的帮助解决了,我相信我可以将这次所学到的知识和技巧运用到以后的编程中。

本方案有个地方可以再改进,就是在农民花费的问题上,可以将图形改为有向图,然后为

每边加上一个花费的权值,最后就可以求得在所有的初始态到终态的路径中,取出一条最短的路径。

    根据广度优先搜索的思想,还可以解决布线迷宫问题和分油问题,当然这需要对程序进行适当的修改,但是总体的思想可以互相运用。

七、 参考文献

  数据结构课程设计案例精编   李建学等人 清华大学出版社 2007.2出版  P192~P198

  数据结构C语言描述         严蔚敏等人 清华大学出版社 1999.3出版  P579~P595

  数据结构与程序设计—C语言  敖福江等人 清华大学出版社 2004.12出版 P427~P434

  (还有网络的参照)

                                                                                     

指导教师:                日期                      实验成绩:  

原创粉丝点击