修道士与野人问题

1、问题描述:这是一个古典问题.假设有n个道士和n个野人准备渡河.但只有一条能容纳c人的小船,为了防止野人侵犯修道士,要求无论在何处,修道士的个数不得少于野人的个数(除非修道士个数为0).如果两种人都回会划船,设计一个算法,确定他们能否过河.若能,则给出小船来回次数最少的最佳方案.
2、设计

2.1 设计思想

(1)存储结构

typedef struct

{

    int xds;  //修道士

    int yr;   //野人

    int cw;   //船的位置

}DataType;  //表示当前状态的结构体

typedef struct node

{

        DataType data;

        struct node *son;

        struct node *bro;

        struct node *par;

        struct node *next;

}Ltable;   //定义的邻接表

(2)主要算法的基本思想

1.用一个三元组(x1,x2,x3)表示渡河过程中各个状态。x1表示起始岸上修道士的个数,x2为起始岸上野人个数,x3表示小船位置(0--在目的岸上,1--在起始岸上)。

2.根据给出的小船上的位置数量,生成小船上的安全状态,即在船上的时候修道士的人数也要比野人的数量要多(除非修道士人数为0)。渡船优先规则:起始岸一次运走的人越多越好(即起始岸运多人优先),同时野人优先运走;目的岸一次运走的人越少越好(即目的岸运少人优先),同时修道士优先运走;

3.累似于操作系统中的银行家算法的安全性检测,即让修道士跟野人上船后,检测当船到达对岸后,两岸修道士的安全状态,若修道士安全,则将此结点加入到邻接表中。

4..采用广度搜索,得到首先搜索到的边数最少的一条通路。
5.
若问题有解,输出一个最佳方案。无解给出失败信息。

2.2设计表示法

(1)过程或函数调用关系

main()->Ltableinit()->insertson()->work() ;

work()->findfa()->jiancha ()->insertson()->insertbro()->print() ;

(2)基于数据结构的操作组

       Ltableinit() , insertson() , insertbro() ;

(3) 过程或函数接口规格说明

void Ltableinit(Ltable **head)  //初始化邻接表

void insertson(Ltable *head, DataType x)  //将元素x作为儿子结点插入到邻接表

void insertbro(Ltable *head,DataType x)  //将元素x作为兄弟结点插入到邻接表

int findfa(DataType x,int n)  //生成船上的修道士安全的状态

int jiancha(DataType x,int n)   //安全性检测,检测此时刻,两岸修道士是否安全

void print(Ltable *q,Ltable *p)     //打印路径

2.3       详细设计

void work(Ltable *p,int n,int c)

{

       Ltable *q,*t;

       DataType tem;

       int i,flag,flag1,g=0,j,count=0;

       q=p->son;

       while (q!=NULL)

       {

              flag=0;

        j=findfa(q->data,c);

     

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

              {

                     tem.xds=q->data.xds-fa[i].xds;

                     tem.yr=q->data.yr-fa[i].yr;

                     tem.cw=1-q->data.cw;

                     t=q;

                    

                     if (jiancha (tem,n))

                     {

                            flag1=1;

                while (t!=p)

                            {

                                   if(tem.xds== t->data.xds&&tem.yr==t->data.yr&&tem.cw==t->data.cw)

                                   {

                                          flag1=0;

                                          break;               

                                   }

                                   t=t->par;

                            }

                if(flag1==1)

                            {

                                   if (flag==0)

                                   {

                                          insertson(q, tem);

                                          flag=1;

                                   }

                                   else

                           insertbro(q,tem);                                                    

                                   if (tem.xds==0&&tem.yr==0&&tem.cw==0)

                                   {

                                          print(q,p);

                                          count++;

                                   }

                            }                  

                     }

              }   

              q=q->next;

       } 

       if (count==0)

              printf("无法成功渡河,修道士好郁闷!/n");

       else

              printf("%d种渡河方式。/n",count);

}

 

3.调试分析

(1)在刚开始做的时候,建立邻接表这部分感觉有点困难,直到现在还感觉自己建的图更像是树,由于要建成邻接表,于是我设了三个指针,son(儿子指针),bro(兄弟指针),par(双亲指针),层与层之间是用son指针建立连接的,同一层的元素用bro指针建立连接,同时每一个结点都指向上一层它们的根结点(父亲结点)。

(2)在执行的时候发现存在死循环,通过断点跟踪后,发现会存在一状态跟之前有过的状态重复,从而就导致了死循环的产生,找到原因后,我把这种情况处理了一个,解决了这个问题。

 

4.用户手册: 本程序运行环境为DOS,执行文件为:yeren.exe.

进入演示程序后,即出现提示信息:请输入修道士与野人的人数n:请输入船可容纳的人数c:输入后程序执行相应操作后,显示相应结果。

 

5.源代码

   

#include <stdio.h>

#include <malloc.h>

#include <stdlib.h>

typedef struct

{

    int xds;  //xiudaoshi

    int yr;   //yeren

    int cw;   //chuanwei

}DataType;

DataType fa[50000];

typedef struct node

{

    DataType data;

    struct node *son;

    struct node *bro;

    struct node *par;

    struct node *next;

}Ltable;

void Ltableinit(Ltable **head)    //初始化邻接表的操作

{

    *head=(Ltable *)malloc(sizeof (Ltable));  //动态分配空间

    (*head)->son=NULL;

    (*head)->bro=NULL;

    (*head)->par=NULL;

    (*head)->next=NULL;

}

void insertson(Ltable *head, DataType x)   //在邻接表中插入儿子结点的操作

{

    Ltable *q,*s;

    q=(Ltable *)malloc(sizeof (Ltable));

    q->data=x;

    head->son=q;

    s=head;

    while (s->next!=NULL)

    s=s->next;

    q->par=head;

    q->son=NULL;

    q->bro=NULL;

    s->next=q;

    q->next=NULL;

}

void insertbro(Ltable *head,DataType x)     //在邻接表中插入兄弟结点的操作,所有的兄弟结点都指向他们右边的结点;

{

    Ltable *q,*s;

    q=(Ltable *)malloc(sizeof (Ltable));

    s=head->son;

    q->data=x;

    while (s->bro!=NULL)

       s=s->bro;

    s->bro=q;

    s->next=q;

    q->next=NULL;

    q->bro=NULL;

    q->par=head;

    q->son=NULL;

}

 

int findfa(DataType x,int n)     //生成在船上修道士仍安全的几种情况;

{

    int i=0,a,b,t=0;

    if(x.cw)

    {

       a=0;b=n-a;

       while (a+b>=1)

           {

              t++;

              while (b>=0)

              {

                 

                  fa[i].xds=a;

                  fa[i].yr=b;

                  i++;

                  a++;

                  b--;

              }

              a=0;

              b=n-a-t;

           }

    }

    else

    {

       a=1;b=0;t=0;

       while (a+b<=n)

       {

           t++;

           while (a>=0)

           {               

              fa[i].xds=a*(-1);

              fa[i].yr=b*(-1);

              i++;

              a--;

              b++;

           }

           a=fa[0].xds*(-1)+t;

           b=0;

       }

    }

    return i; 

}

 

int jiancha(DataType x,int n)     //安全性检测,检查当前情况下,修道士是否安全

{

    if ((x.xds>=x.yr||x.xds==0)&&((n-x.xds)>=(n-x.yr)||x.xds==n)&&x.xds>=0&&x.xds<=n&&x.yr>=0&&x.yr<=n)

       return 1;

    else

       return 0;

}

 

void print(Ltable *q,Ltable *p)     //打印安全渡河的过程

{

    DataType a[100];

    int i=1;

    a[0].cw=0;

    a[0].xds=0;

    a[0].yr=0;

    while (q!=p)

    {

       a[i++]=q->data;

       q=q->par;

    }

    while ((--i)>-1)    

    {

          printf("( %d %d %d )",a[i].xds,a[i].yr,a[i].cw);

          if (!(a[i].xds==0&&a[i].yr==0&&a[i].cw==0))

          {if (a[i].cw==1)

          printf(" --> ( %d %d ) --> ( %d %d 0 )/n",a[i].xds-a[i-1].xds,a[i].yr-a[i-1].yr,a[i-1].xds,a[i-1].yr);

          else printf(" <-- ( %d %d ) <-- ( %d %d 1 )/n",(a[i].xds-a[i-1].xds)*(-1),(-1)*(a[i].yr-a[i-1].yr),a[i-1].xds,a[i-1].yr);

          }

          else printf("/n");

    }

    printf("渡河成功!/n");

}

 

void work(Ltable *p,int n,int c)

{

    Ltable *q,*t;

    DataType tem;

    int i,flag,flag1,g=0,j,count=0;

    q=p->son;

    while (q!=NULL)

    {

       flag=0;

        j=findfa(q->data,c);

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

       {

           tem.xds=q->data.xds-fa[i].xds;

           tem.yr=q->data.yr-fa[i].yr;

           tem.cw=1-q->data.cw;

           t=q;

           if (jiancha (tem,n))

           {

              flag1=1;

                while (t!=p)

              {

           if(tem.xds==t->data.xds&&tem.yr==t->data.yr&&tem.cw==t->data.cw)

                  {

                     flag1=0;

                     break;               

                  }

                  t=t->par;

              }

                if(flag1==1)

              {

                  if (flag==0)

                  {

                     insertson(q, tem);

                     flag=1;

                  }

                  else

                           insertbro(q,tem);                         

                  if (tem.xds==0&&tem.yr==0&&tem.cw==0)

                  {

                     print(q,p);

                     count++;

                  }

              }         

           }

       } 

       q=q->next;

    } 

    if (count==0)

       printf("无法成功渡河,修道士好郁闷!/n");

    else

       printf("%d种渡河方式。/n",count);

}

int main()

{

    Ltable *p;

    DataType tem;

    Ltableinit(&p);        //初始化邻接表;

    int n,c;

    while (1)

    {

       printf("请输入修道士与野人的人数n:/n");

       scanf("%d",&n);

       if (n==0)

           break;

       printf("请输入船可容纳的人数c:/n");

       scanf("%d",&c);

       tem.xds=n;

       tem.yr=n;

       tem.cw=1;

       insertson(p, tem);           //将初始状态作为头结点的孩子结点;

       work(p,n,c);                 //进行广度搜索;

    }

    return 1;

}

 

 

 
  • 9
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值