农夫过河问题

 一、需求分析

1.问题描述:

一个农夫带着一只狼、一只羊和一棵白菜,身处河的南岸。他要把这些东西全部运到北岸。他面前只有一条小船,船只能容下他和一件物品,另外只有农夫才能撑船。如果农夫在场,则狼不能吃羊,羊不能吃白菜,否则狼会吃羊,羊会吃白菜,所以农夫不能留下羊和白菜自己离开,也不能留下狼和羊自己离开,而狼不吃白菜。

2.基本要求:                        

(1)利用图的存储结构

(2)图的搜索算法

(3)求出农夫将所有的东西运过河的所有方案    

View Code
#include<stdio.h>
#define M 200
int a=0;//遍寻下一条路径
int b=0;//方案中的值
int c[4],d[4];
int Fa[M*M];//保存方案
int N;//有效的顶点数
typedef struct
{
    int Vertices[M];//存放顶点(比N少)
    int edge[M][M];//存放边的邻接矩阵
    int count;
}AdjMGraph;
typedef struct stack
{
  int top;
  int node[M*M];
}Path;//路径栈
int Judge(int a,int b)
{
    int i;
    int j=0;
    //int max;
    c[0]=a/8;     d[0]=b/8;
    a=a%8;        b=b%8;
    c[1]=a/4;     d[1]=b/4;
    c[2]=(a%4)/2; d[2]=(b%4)/2;
    c[3]=a%2;     d[3]=b%2;
    //printf("%d %d %d  %d %d %d\n",c[0],c[1],c[2],d[0],d[1],d[2]);N
    if(c[0]==d[0])return 0;
    //else if (c[0]>d[0]){max=c[0];c[0]=d[0];d[0]=max;}
    for(i=1;i<4;i++)
    {
        if(c[i]!=d[i])j++;
    }
    if(j<=1)return 1;
    return 0;
}
void Initiate(AdjMGraph *G,int n)//邻接矩阵的建立
{
    int i,j;
    G->count=n;
    for(i=0;i<n;i++)
     for(j=0;j<=i;j++)//初始化
     {
         if(Judge(G->Vertices[i],G->Vertices[j]))
        {

             G->edge[i][j]=1;
             G->edge[j][i]=1;
        }
         else
        {
             G->edge[i][j]=0;
             G->edge[j][i]=0;
        }
      }
}

int Push(Path *path,int m)      //入栈
{
    path->node[path->top]=m;
    path->top++;
    return 0;
}
int Top(Path *path ,int *m)    //读出栈顶值
{
    *m=path->node[path->top-1];
    return *m;
}
int GetTop(Path *path,int *m)   //出栈
{
    --path->top;
    *m=path->node[path->top];
    return 0;
}
int printfpath(Path *path)    //复制出出栈中所有值
{
    int i;
    i=0;
    if(path->top==8&&path->node[path->top-1]==9)
    {
       while(i<path->top)
      {

        Fa[b]=path->node[i];
        //printf("%d ",Fa[b]);
        i++;
        b++;
      }
    }
    return 0;
}
void Printf(AdjMGraph *G)//辅助:邻接矩阵输出
{
    int i,j;
    for(i=0;i<G->count;i++)
    {
     for(j=0;j<G->count;j++)
     {
         printf("%d ",G->edge[i][j]);
     }
     printf("\n");
     }
}
int DFS(AdjMGraph *G,Path *path,int x,int t)//
{
       int i;
       int k,p,m;
       Push(path,x);//遍历过的点入栈
       //printfpath(path);
       //printf("\n");
       p=0;
       k=0;          //路径未遍历完


       for(i=t;i<N;i++) //搜相关联的顶点
       if(G->edge[i][x]>0)
      {
          k=1;
          G->edge[i][x]=0;
          G->edge[x][i]=0; //此边已访问,删除此边
          DFS(G,path,i,0);//找到的是与x关联的i,寻找下一条关联的边
          break;
      }
       if(k==0)       //遍历完当前顶点关联的边
       {
              //if(GetTop(path,&m)==9)jishu++;
              //if(jishu==2)return 0;
              printfpath(path);
                                                                    // printf("\n");
              while(path->top!=0&&p==0&&a<N-1)//a为点数减1
              {
                  GetTop(path,&m);                                 //printf("\n\n%d ",m);//取出
                  Top(path,&x);                                    // printf("\n\n%d ",x);//读出
                  for(i=0;i<N;i++)
                  if(G->edge[i][x]>0)
                 {
                  p=1;
                  k=1;                   //一条路径未遍历完
                  if(x==0)               //从新开始遍历
                  {
                      G->edge[x][m]=1;
                      G->edge[m][x]=1;
                      GetTop(path,&x);
                   }
                  G->edge[x][m]=1;G->edge[m][x]=1;
                    a=++m;            //如果可能的话,从当前节点的下一条关联边开始搜寻
                                                                  // printf("%d***\n\n",a);
                  GetTop(path,&x);
                                                                 //Printf(G);///
                  DFS(G,path,x,a);//x被入栈
                  break;
                 }                                             //printf("%d***\n\n",a);
               G->edge[x][m]=1;G->edge[m][x]=1;//恢复在上一层中被删除的边
              }
       }
       return 0;
}
void Ch(int i)
{
    switch(i)
    {
        case 0:printf("");break;
        case 1:printf("");break;
        case 2:printf("");break;
        case 3:printf("");break;
    }

}
int main()
{
    int i,j,k;
    int dx1,dx2,dx3,dx4;
    j=0;
    k=0;
    AdjMGraph graph;
    Path path;
    int a[20];//记不合理的状态,做检验用
    //for(;k>0;k--)printf("%d ",graph.Vertices[k-1]);
    //for(;j>0;j--)printf("\n%d ",a[j-1]);
    /*踢出状态1001 1000 0111 0110 1100 0011*/
    /*for(i=0;i<10;i++)//踢出不合法的状态
    {
        if(i<3)graph.Vertices[i]=i;
        else if(i>2&&i<5)graph.Vertices[i]=i+1;
        else if(i>4&&i<7)graph.Vertices[i]=i+5;
        else graph.Vertices[i]=i+6;
    }*/                                                          //Printf(&graph);
    path.top=0;
    printf("\t\t#####################################\n");
    printf("\t\t#####  东西(a,b,c,d)的状态说明   ####\n");
    printf("\t\t#####   0代表人");
    printf("\t\t1代表狼  ####\n");
    printf("\t\t#####   2代表羊");
    printf("\t\t3代表菜  ####\n");
    printf("\t\t#####  a,b,c,d取0表示在河的南岸  ####\n");
    printf("\t\t#####  a,b,c,d取1表示在河的北岸  ####\n");
    printf("\t\t#####################################\n");
    printf("\t\t请输入无人时不能在同一侧的东西代号:\n\t\t");
    scanf("%d %d",&dx1,&dx2);
    printf("\t\t");
    scanf("%d %d",&dx3,&dx4);
    while(dx1!=1 || dx2!=2 || dx3!=2 || dx4!=3)
    {
        printf("\n\t\t错误!请输入无人时不能在同一侧的东西代号:\n\t\t");
        scanf("%d %d",&dx1,&dx2);
        printf("\t\t");
       scanf("%d %d",&dx3,&dx4);
    }
    for(i=0;i<16;i=i+2)
    {
        Judge(i,i+1);//i为C[],i+1为d[];
        if(c[0]!=c[dx1] && c[dx1]==c[dx2])a[j++]=i;
        else if(c[0]!=c[dx3] && c[dx3]==c[dx4])a[j++]=i;
        else graph.Vertices[k++]=i;
        if(d[0]!=d[dx1] && d[dx1]==d[dx2])a[j++]=i+1;
        else if(d[0]!=d[dx3] && d[dx3]==d[dx4])a[j++]=i+1;
        else graph.Vertices[k++]=i+1;
    }
    //printf("%d\n\n",k);
    N=k;
    Initiate(&graph,k);
    DFS(&graph,&path,0,0);
    for(i=0;i<b;i=i+2)
    {
        Judge(graph.Vertices[Fa[i]],graph.Vertices[Fa[i+1]]);
        if(graph.Vertices[Fa[i]]==0)printf("\t\tCase %d    南岸(状态)     北岸(状态)\n",(i/8+1));
        printf("\t\t(");
        for(j=0;j<4;j++)
        {
            if(j==3)printf("%d",c[j]);
            else printf("%d,",c[j]);
        }
        printf(")  ");
        for(j=0;j<4;j++)
          if(c[j]==0)Ch(j);
        printf("\t\t\t");
        for(j=0;j<4;j++)
          if(c[j]==1)Ch(j);
        printf("\n");

        printf("\t\t(");
        for(j=0;j<4;j++)
        {
            if(j==3)printf("%d",d[j]);
            else printf("%d,",d[j]);
        }
        printf(")  ");
        for(j=0;j<4;j++)
          if(d[j]==0)Ch(j);
        printf("\t\t\t");
        for(j=0;j<4;j++)
          if(d[j]==1)Ch(j);
        printf("\n");

    }
//int Fa[N*N];int c[4],d[4];
//int Judge(,int b)
    //printfpath(&path);
    return 0;
}

                      6.农夫过河问题

 

一、需求分析

1.问题描述:

一个农夫带着一只狼、一只羊和一棵白菜,身处河的南岸。他要把这些东西全部运到北岸。他面前只有一条小船,船只能容下他和一件物品,另外只有农夫才能撑船。如果农夫在场,则狼不能吃羊,羊不能吃白菜,否则狼会吃羊,羊会吃白菜,所以农夫不能留下羊和白菜自己离开,也不能留下狼和羊自己离开,而狼不吃白菜。

2.基本要求:                        

(1)利用图的存储结构

(2)图的搜索算法

(3)求出农夫将所有的东西运过河的所有方案    

          

二、设计

      1. 设计思想

    (1)存储结构

         以邻接矩阵存储合理状态,用一个一维数组保存所有的方案;

(2)主要算法基本思想

       人,狼,羊和白菜共有2的四次方16种状态(河岸的状态可由人的状态确定),去掉不允许出现的6种状态,10个状态对应矩阵的10个结点值,然后根据状态的连续改变初始化矩阵,接着就用递归的深度优先法搜索所有的路径,即求出过河的方案。

main

 

 

    2. 设计表示

        (1)函数调用关系图

    main→Judge→Initiate→DFS→Push→StackPop→Top→GetTop→           printfpath→Ch

 

 
  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  (2)函数接口规格说明

int Judge(int a,int b)  //将16种状态通过a,b输入,去掉不允许的6种

int GetTop(Path *path,int *m)   //出栈

int Push(Path *path,int m)      //入栈

int Top(Path *path ,int *m)    //读出栈顶值

void Initiate(AdjMGraph *G,int n)//邻接矩阵顶点数为n的邻接矩阵G的建立

int DFS(AdjMGraph *G,Path *path,int x,int t) 图G中搜索的起始点为X,从t点开始搜索与x关联的顶点,搜索过的点入栈path。

int printfpath(Path *path)    //复制出出栈path中所有值,用FA【】保存

void Printf(AdjMGraph *G)//辅助:邻接矩阵输出,用于观察搜索的过程。

void Ch(int i) //将状态转换为汉字的方案输出

3. 详细设计(主要函数)  

【1】int Judge(int a,int b)

将十进制的16种状态分别转换为二进制的数存在一维数组中

   判断:

       人不在时,羊狼,羊菜不能再一起

从而初始化了矩阵的结点值

【2】Initiate(&graph,k);

通过Judge函数判断可以连续变化的状态

   在Judge中人的状态必须一次一遍,而其他东西的状态,最多变一个。根据其返回值将矩阵初始化。

【3】DFS(AdjMGraph *G,Path *path,int x,int t)

递归的深度优先搜索法

int a=0;//遍寻下一条路径

int DFS(AdjMGraph *G,Path *path,int x,int t)

{

       int i;

       int k,p,m;

        Push(path,x);//遍历过的点入栈

        p=0;

       k=0;          //路径未遍历完

       for(i=t;i<N;i++) //搜相关联的顶点

       if(G->edge[i][x]>0)

      {

          k=1;

          G->edge[i][x]=0;

          G->edge[x][i]=0; //此边已访问,删除此边

          DFS(G,path,i,0);//找到的是与x关联的i,寻找下一条关联的边

          break;

      }

       if(k==0)       //遍历完当前顶点关联的边

       {

              printfpath(path);                                                              

                       while(path->top!=0&&p==0&&a<N-1)//a为点数减1

              {

                                GetTop(path,&m);                                

             Top(path,&x);                                                                           for(i=0;i<N;i++)

             if(G->edge[i][x]>0)

                          {

                                p=1;

                                k=1;                   //一条路径未遍历完

                                if(x==0)               //从新开始遍历

                                {

                                    G->edge[x][m]=1;

                                    G->edge[m][x]=1;

                                    GetTop(path,&x);

                   }

                  G->edge[x][m]=1;G->edge[m][x]=1;

                            a=++m;//如果可能的话,从当前节点的下一条关联边开始搜寻

              GetTop(path,&x);

                                DFS(G,path,x,a);//x被入栈

                                break;

                          }                                           

                      G->edge[x][m]=1;G->edge[m][x]=1;//恢复在上一层中被删除的边

                       }

       }

       return 0;

}

三、调试分析

      1.调试过程中遇到的主要问题是如何解决的;

      该题有两大难点,一是:矩阵的合理逻辑构建

                      二是:两条方案的搜出

       刚开始由于平常做ACM题图论的影响,没有正确把握题意,曲解了题意,在老师的提示下正确的理解了题意。

       由于搜图函数已在理解错误下正确写出,所以分析了如何逻辑构图之后,问题很容易就得到解决。在老师的指导下,程序越来越完善。   

2.对设计和编码的回顾讨论和分析;

在遇到每个问题的时候,一定要认真冷静的分析题意,以免通过过往经验而误读题意,最后导致与正确结果南辕北辙的后果。

弄懂题意后,要先从程序的整体入手,分析写程序的难点所在,然后联系所学找到可能解决的途径,选出最佳的方法攻破难点。

最后还等考虑程序的安全性,尤其是健壮性的问题,使程序更加的完善,如有可能最好能解决题目之外类似的问题。

3.时间和空间复杂度的分析;

【1】:图的初始化算法 时间O(n*n),空间O(1)

       【2】:图的搜索算法 时间O(n*n),  空间O(1)

      4.改进设想;

       程序能实现预期功能,可有改进空间

【1】:图更简洁初始化方法

       【2】:更能解决一般性的问题

5.经验和体会等。

多参加有益的比赛,科研,动手编程,才能熟练灵活的掌握C语言基础知识,才能更好的理解掌握数据结构的精髓。从而避免基础语法错误,让代码变得更简洁高效。如此才能准确高效的解决问题。

 

四、用户手册(即使用说明)

   仅需按照提示的输入即可。若出错,则重新来过。

 

五、运行结果

运行环境:codeblocks

测试用表:1 2 3 4

          0 1 2 3

          1 2 2 3

运行后的界面

 

测试数据:1 2 3 4

 

**输入不符合要求或不满足题意时,无法输出正确结果

****在输入完后,核对输入是否符合题意,如若不然提示出错,重新输入

(已改正)测试数据:1 2 3 4

 

正确测试数据:1 2 2 3

 

六、源程序清单

所有函数均在一个 农夫过河.c 文件中

          

转载于:https://www.cnblogs.com/XDJjy/archive/2013/04/07/3006024.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值