修道士與野人問題

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;

}

 

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章