基于C++的农夫过河问题算法设计与实现方法

本文实例讲述了基于C++的农夫过河问题算法设计与实现方法。分享给大家供大家参考,具体如下:

问题描述:

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

实现上述求解的搜索过程可以采用两种不同的策略:一种广度优先搜索,另一种深度优先搜索。这里介绍在广度优先搜索方法中采用的数据结构设计。

程序源码:

/***********************************************
 * 农夫过河问题(P64 队列的应用)
 * 约定:用四位二进制数分别顺序表示农夫、狼、白菜和羊的状态
 *   即:{dddd} <=> {Farmer, Wolf, Cabbage, Goat} 其中:d={0,1}
 * 说明:0表示在东岸 1表示在西岸,初始状态为0000,终止状态为1111
************************************************/
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 16
typedef int EntryType;
typedef struct queue
{
  EntryType data[MAXSIZE];
  int front,rear;    //front队头,rear队尾
}SeqQueue, * SeqQueuePtr;
// 创建空队列
SeqQueuePtr create_sequeue(void)
{
  SeqQueuePtr pque;
  pque = (SeqQueuePtr)malloc(sizeof(SeqQueue));
  if(pque){
    pque->front = 0;
    pque->rear = 0;
  }
  else{
    printf("Error: malloc() failed, out of memory!\n");
  }
  return(pque);
}
int is_queEmpty(SeqQueuePtr pque)
{
  return( pque->front == pque->rear );
}
int is_queFull(SeqQueuePtr pque)
{
  return( (pque->rear+1)%MAXSIZE == pque->front);
}
// 入队
int enqueue(SeqQueuePtr pque, EntryType x)
{
  if(is_queFull(pque)){
    printf("Queue Overflow Error: trying to add an element onto a full queue\n");
    return 1;
  }
  else{
    pque->data[pque->rear] = x;
    pque->rear = (pque->rear + 1) % MAXSIZE;
    return 0;
  }
}
// 队首元素出队(返回0表示出队异常,出队操作前队列为空)
int dequeue(SeqQueuePtr pque, EntryType * e)
{
  if(is_queEmpty(pque)){
    printf("Empty Queue.\n");
    return 0;
  }
  else{
    *e = pque->data[pque->front];
    pque->front = (pque->front + 1) % MAXSIZE;
    return 1;
  }
}
int is_farmer_crossed(int state)
{
  return ((state & 0x08) != 0);
}
int is_wolf_crossed(int state)
{
  return ((state & 0x04) != 0);
}
int is_cabbage_crossed(int state)
{
  return ((state & 0x02) != 0);
}
int is_goat_crossed(int state)
{
  return ((state & 0x01) != 0);
}
// 若状态相容(安全)则返回1,否则返回0
int is_safe(int state)
{
  if((is_goat_crossed(state) == is_cabbage_crossed(state)) &&
    (is_goat_crossed(state) != is_farmer_crossed(state))) // 羊菜同岸且农夫不在场
    return(0);
  if((is_goat_crossed(state) == is_wolf_crossed(state)) &&
    (is_goat_crossed(state) != is_farmer_crossed(state))) // 狼羊同岸且农夫不在场
    return(0);
  return(1);
}
void river_crossing_problem()
{
  int route[16];      // 记录已经考虑过的状态
  int state;        // 记录当前时刻的状态(状态编号的二进制形式即状态本身)
  int aftercross;     // 记录渔夫当前的选择(渡河对象)会导致的结果状态
  int passenger;      // 临时变量,用于表达农夫的选择(对应二进制位为1表示选中该乘客)
  int results[16]={0};   // 输出结果
  int counter, i;
  SeqQueuePtr states_que; //
  states_que = create_sequeue(); // 创建“状态”队列
  enqueue(states_que,0x00);   // 初始状态0000入队
  for(int i = 0; i < 16; i++){
    route[i] = -1;
  }
  //route[0] = 0;
  while(!is_queEmpty(states_que) && (route[15] == -1))
  {
    if( !dequeue(states_que, &state) ){
      printf("Error: dequeue() - the queue is empty\n");
    }
    // 依次考虑农夫可能的选择:携带羊、白菜和狼,以及农夫只身渡河
    for( passenger = 1; passenger<= 8; passenger <<= 1 )
    {
      // 由于农夫总是在过河,随农夫过河的也只能是与农夫同侧的东西
      if(((state & 0x08) != 0) == ((state & passenger) != 0)){
        // 如果农夫与当前乘客在河岸的同一侧
        aftercross = state^( 0x08|passenger ); // 渡河后的情况
        if(is_safe(aftercross) && (route[aftercross] == -1)){
          // 如果渡河后状态安全,则将其状态入队
          route[aftercross] = state; // 将当前状态的索引记录到路径数组中(下标索引为后续状态值)
          enqueue(states_que, aftercross);
        }
      }
    }//end for()
  }//end while()
  // 输出过河策略:0表示在东岸 1表示在西岸,初始状态为0000,终止状态为1111
  if(route[15] != -1)
  {
    //printf("The reverse path is:\n");
    counter = 0;
    for(state = 15; state != 0; state = route[state]){
      //printf("The state is: %d \n",state);
      results[counter] = state;
      counter++;
      //if(state == 0) return;
    }
    for(i = 0; i< counter; i++){
      state= results[i];
      aftercross = results[i+1];
      if(state & 0x08){
        printf("农夫从东岸到西岸:");
      }
      else{
        printf("农夫从西岸到东岸:");
      }
      switch(state^aftercross ){
      case 12:
        printf("把狼带过河\n");
        break;
      case 10:
        printf("把菜带过河\n");
        break;
      case 9:
        printf("把羊带过河\n");
        break;
      default:
        printf("什么也不带\n");
        break;
      }
    }
  }
  else{
    printf("No solution for this problem.\n");
  }
}
int main(void)
{
  river_crossing_problem();
  system("pause");
  return 0;
}

运行结果:

希望本文所述对大家C++程序设计有所帮助。

(0)

相关推荐

  • C++回溯法实例分析

    本文实例讲述了C++的回溯法,分享给大家供大家参考之用.具体方法分析如下: 一般来说,回溯法是一种枚举状态空间中所有可能状态的系统方法,它是一个一般性的算法框架. 解向量a=(a1, a2, ..., an),其中每个元素ai取自一个有限序列集Si,这样的解向量可以表示一个排列,其中ai是排列中的第i个元素,也可以表示子集S,其中ai为真当且仅当全集中的第i个元素在S中:甚至可以表示游戏的行动序列或者图中的路径. 在回溯法的每一步,我们从一个给定的部分解a={a1, a2, ..., ak}开始

  • 采用C++实现区间图着色问题(贪心算法)实例详解

    本文所述算法即假设要用很多个教室对一组活动进行调度.我们希望使用尽可能少的教室来调度所有活动.采用C++的贪心算法,来确定哪一个活动使用哪一间教室. 对于这个问题也常被称为区间图着色问题,即相容的活动着同色,不相容的着不同颜色,使得所用颜色数最少. 具体实现代码如下: //贪心算法 #include "stdafx.h" #include<iostream> #define N 100 using namespace std; struct Activity { int n

  • C++实现八皇后问题的方法

    本文实例展示了C++实现八皇后问题的方法,是数据结构与算法中非常经典的一个算法.分享给大家供大家参考之用.具体方法如下: 一般在八皇后问题中,我们要求解的是一个8*8的国际象棋棋盘中,放下8个皇后且互相不能攻击的排列总数.皇后的攻击范围为整行,整列,以及其斜对角线. 由于皇后的攻击范围特性,注定我们每行只能放下一个皇后,于是我们要做的只是逐行放下皇后.八皇后问题是回溯法的典型问题.这里我们用的方法很简单: 从第一行开始逐个检索安全位置摆放皇后,一旦有安全位置则考虑下一行的安全位置.如果发现某行没

  • 八皇后问题的相关C++代码解答示例

    八皇后问题即指在一个8*8的棋盘上放置8个皇后,不允许任何两个皇后在棋盘的同一行.同一列和同一对角线上.关键字:递归.上溯.通用技巧: 经观察发现,对8 x 8的二维数组上的某点a[i][j](0<=i,j<=7) 其主对角线(即左上至右下)上的每个点的i-j+7的值(范围在(0,14))均相等: 其从对角线(即右上至左下)上的每个点的i+j的值(范围在(0,14))均相等: 且每个主对角线之间的i-j+7的值均不同,每个从对角线之间的i-j+7的值亦不同: 如a[3][4]: 主:3-4+7

  • c++递归实现n皇后问题代码(八皇后问题)

    还是先来看看最基础的8皇后问题: 在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 扩展到N皇后问题是一样的.一看,似乎要用到二维数组.其实不需要.一维数组就能判断,比如Arr[i],就可以表示一个元素位于第i行第Arr[i]列--应用广泛的小技巧.而且在这里我们不用考虑去存储整个矩阵,如果Arr[i]存在,那么我们在打印的时候,打印到皇后位置的时候输出1,非皇后位输出0即可. 这种思路的实现方式网上大把,包括前面提到的那

  • C++三色球问题描述与算法分析

    本文实例讲述了C++三色球问题.分享给大家供大家参考,具体如下: /* * 作 者:刘同宾 * 完成日期:2012 年 11 月 15 日 * 版 本 号:v1.0 * * 输入描述: * 问题描述:三色球问题:若一个口袋中放有12个球,其中有3个红的.3个白的和6个黒的,问从中任取8个共有多少种不同的颜色搭配? * 提示: 设任取的红球个数为i,白球个数为j,则黒球个数为8-i-j,根据题意红球和白球个数的取值范围是0~3, * 在红球和白球个数确定的条件下,黒球个数取值应为8-i-j<=6.

  • C++基于回溯法解决八皇后问题示例

    本文实例讲述了C++基于回溯法解决八皇后问题的方法.分享给大家供大家参考,具体如下: 回溯法的基本做法是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法.这种方法适用于解一些组合数相当大的问题. 回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树.算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解.如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯:否则,进入该子树,继续按深度优先策略搜索. 回溯法指导思想--走不通,就掉头.设计过程:确

  • C语言使用回溯法解旅行售货员问题与图的m着色问题

    旅行售货员问题 1.问题描述: 旅行售货员问题又称TSP问题,问题如下:某售货员要到若干个城市推销商品,已知各城市之间的路程(或旅费),他要选定一条从驻地出发,经过每个城市一遍最后回到驻地的路线,使总的路线(或总的旅费)最小.数学模型为给定一个无向图,求遍历每一个顶点一次且仅一次的一条回路,最后回到起点的最小花费. 2.输入要求: 输入的第一行为测试样例的个数T( T < 120 ),接下来有T个测试样例.每个测试样例的第一行是无向图的顶点数n.边数m( n < 12,m < 100 )

  • 基于C++的农夫过河问题算法设计与实现方法

    本文实例讲述了基于C++的农夫过河问题算法设计与实现方法.分享给大家供大家参考,具体如下: 问题描述: 一个农夫带着-只狼.一只羊和-棵白菜,身处河的南岸.他要把这些东西全部运到北岸.他面前只有一条小船,船只能容下他和-件物品,另外只有农夫才能撑船.如果农夫在场,则狼不能吃羊,羊不能吃白菜,否则狼会吃羊,羊会吃白菜,所以农夫不能留下羊和白菜自己离开,也不能留下狼和羊自己离开,而狼不吃白菜.请求出农夫将所有的东西运过河的方案. 实现上述求解的搜索过程可以采用两种不同的策略:一种广度优先搜索,另一种

  • 基于JSP的RSS阅读器的设计与实现方法(推荐)

    阅读器访问地址:http://easyrss.tk/,欢迎体验! 阅读导览 一. 概述 二. 设计的基本概念和原理 三. 设计方案 四. 主要源代码 五. 阅读器使用说明 概述 获得信息是在人类的生活中是必不可少的环节.如果现在的社会对获得信息不快捷,那么这个社会将不会像如今这般的发达和进步.在当今网络技术相当发达的今天,大量的信息充斥在网上.现在网络越来越发达,用户在网上既能工作也能娱乐.当用户在网上需浏览很多个网站才能获取自己多需的信息时,那就感觉很累.因为现在每个网站都有很多信息,找到自己

  • C++基于人工智能搜索策略解决农夫过河问题示例

    本文实例讲述了C++基于人工智能搜索策略解决农夫过河问题.分享给大家供大家参考,具体如下: 问题描述 一农夫带着一头狼,一只羊和一个白菜过河,小船只能一次装载农夫和一样货物,狼会吃羊,羊会吃白菜,只有农夫在时才安全.现欲让所有物品包括农夫都安全过道河对岸,求最佳答案. 状态空间 用16*4的矩阵:a[16][4],存放每一步的状态,第一列表示农夫的状态,第二列表示菜的状态,第三列表示羊的状态,第四列表示狐狸的状态,数组a里面的元素只为0或1,0代表在左岸,1代表在右岸. 初始状态a[0][0]=

  • Java简单实现农夫过河问题示例

    本文实例讲述了Java简单实现农夫过河问题.分享给大家供大家参考,具体如下: 一.问题描述 老伯伯要带鱼.狗.猫过河到对岸.,有一条船,只能坐一个人,老伯每次只能带一样动物过河,当老伯不在的时侯狗会咬猫,猫会吃鱼.,请问怎么顺序过河呢? 二.实现代码 package demo; import java.util.ArrayList; import java.util.List; public class CrossRiver { List<String> listThis = new Arra

  • 基于 Python 实践感知器分类算法

    Perceptron是用于二进制分类任务的线性机器学习算法.它可以被认为是人工神经网络的第一种和最简单的类型之一.绝对不是"深度"学习,而是重要的组成部分.与逻辑回归相似,它可以快速学习两类分类任务在特征空间中的线性分离,尽管与逻辑回归不同,它使用随机梯度下降优化算法学习并且不预测校准概率. 在本教程中,您将发现Perceptron分类机器学习算法.完成本教程后,您将知道: Perceptron分类器是一种线性算法,可以应用于二进制分类任务. 如何使用带有Scikit-Learn的Pe

  • C++算法设计之马踏棋盘的实现

    本文实例为大家分享了C++算法设计之马踏棋盘的具体代码,供大家参考,具体内容如下 (一)马踏棋盘经典算法描述: (1)马踏棋盘是经典的程序设计问题之一,主要的解决方案有两种:一种是基于深度优先搜索的方法,另一种是基于贪婪算法的方法.第一种基于深度优先搜索的方法是比较常用的算法,深度优先搜索算法也是数据结构中的经典算法之一,主要是采用递归的思想,一级一级的寻找,遍历出所有的结果,最后找到合适的解.而基于贪婪的算法则是制定贪心准则,一旦设定不能修改,他只关心局部最优解,但不一定能得到最优解. [问题

  • C#算法设计与分析详解

    目录 1. 什么是科学方法?? 1.观察 2.将问题规模和运行时间的关系量化 2.数学模型 近似 近似运行时间 成本模型 总结 3. 增长数量级的分类 4. 倍率实验 5.注意事项 6. 处理对于输入的依赖 7.内存 1. 对象 2. 链表 3. 数组 4. 字符串对象 作为程序员,开发完一段代码,实现了某个功能时,有必要知道: 我的程序需要多长时间? 是什么导致我的程序消耗很多内存? 比如,统计或者处理了一大批数据.影响这些问题的因素很多,例如,电脑的性能,数据的性质(值类型和引用类型的区别)

  • PHP基于递归实现的约瑟夫环算法示例

    本文实例讲述了PHP基于递归实现的约瑟夫环算法.分享给大家供大家参考,具体如下: 约瑟夫环问题: 39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓.于是决定了自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀.然后下一个重新报数,直到所有人都自杀身亡为止.然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏. <?php $nu

  • 基于JavaScript实现的折半查找算法示例

    本文实例讲述了基于JavaScript实现的折半查找算法.分享给大家供大家参考,具体如下: 折半查找也叫做二分查找,是针对有序表的一种查找方式,其思想如下: 将数组的第一个位置设为下边界: 将数组的最后一个位置设为上边界: 如果下边界等于或小于上边界,则做如下操作: 将中点设置为上边界加下边界之和除以二:    如果中点的元素小于查询的值,则将下边界设置为中点元素所在下标加1:    如果中点的元素大于查询的值,则将上边界设置为中点元素所在下标减1:    否则中点元素即为要查找的元素,可以进行

  • 基于JavaScript实现的顺序查找算法示例

    本文实例讲述了基于JavaScript实现的顺序查找算法.分享给大家供大家参考,具体如下: 对于查找数据来说,最简单的方法就是从列表的第一个元素开始对列表元素逐个进行判断,直到找到了想要的结果.这个方法叫做顺序查找,有时候也被叫做线性查找.它属于暴力查找技巧的一种. 顺序查找实现起来非常简单,代码如下: function generalSearch(arr,data){//普通的顺序查找,就是遍历一遍看是否找到 for(var i=0;i<arr.length;i++){ if(arr[i]==

随机推荐