农夫过河问题算法设计与实现

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

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

程序源码:

/***********************************************
 *  农夫过河问题(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;
}

时间: 2024-10-18 08:36:37

农夫过河问题算法设计与实现的相关文章

数据结构设计——农夫过河问题

农夫过河问题 1. 问题描述: 设有一个农夫带一只狼,一只羊和一筐菜来到河边,打算乘一只船从右岸渡到左岸去.该船的负载能力为农夫每次只能带一样东西过河.在无农夫的时候,狼和羊不能在一起,羊和菜不能在一起.设计一个方案,使农夫可以无损失地渡过河. 2. 设计思路: 设计好图的结构,点以(农夫,狼,羊,菜)表示,设置图的点集,边集,点数,边数: 用is_safe函数确定图中各点是否安全,将不安全的点去掉,剩下安全的点,然后判断两点之间是否可变换,再通过层次遍历找到路径,设定好参数,打印出路径: 3.

(转)常用的算法设计与分析-一夜星辰的博客

算法设计与分析 分治法 思想 1. 将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同.递归地解这些子问题,然后将各子问题的解合并得到原问题的解. 2. divide-and-conquer(P) { if(|P| <= n0)adhoc(P); divide P into samller subinstances P1,P2...,Pk; for(int i = 1;i < k;i++) { yi = divide-and-conquer(Pi); } retu

AACOS:基于编译器和操作系统内核的算法设计与实现

AACOS:基于编译器和操作系统内核的算法设计与实现 [计算机科学技术] 谢晓啸 湖北省沙市中学 [关键词]: 编译原理,操作系统内核实现,算法与数据结构,算法优化 0.索引 1.引论 1.1研究内容 1.2研究目的 1.3研究提要 正文 2.1研究方法 2.2编译器部分 2.2.1从计算器程序中得到的编译器制作启示 2.2.2在编译器中其它具体代码的实现 2.2.3编译器中栈的高级应用 2.2.3编译器中树的高级应用 2.2.4编译器与有限状态机 2.3操作系统内核部分 2.3.1操作系统与底

“谁是大V”算法设计 (Map-Reduce TopN)

作业设计与资料 链接: http://pan.baidu.com/s/1o6MJTyi 密码: 628d 一.     作业要求   根据关注列表relsample.json文件,设计MapReduce算法得到被关注次数最多的前十人,即寻找谁是大V. 二.     算法设计   第一个Mapper用于解析json文件: 从relsample.json文件中解析出ids列表的内容,即每个人所关注的人的列表.输出的value为列表中的每一项,k为one. 对应的文件为\src\myMapper\re

【字符串处理算法】将输入字符串中的各个单词反序的算法设计及C代码实现

一.需求描述 输入一个字符串,编写程序将该字符串中的各个单词反序拼装并输出.例如,如果输入的字符串是"Hello, how do you do",那么输出的字符串为"do you do how Hello,".注意保留各个单词之间的空格及相应的标点符号. 二.算法设计 通过观察示例字符串(即"Hello, how do you do"),我们可以看到该字符串中各个单词与空格之间的关系为:单词总数=空格总数+1.也就是说,示例字符串中的空格总数为4

《算法之道》精华 算法设计部分

<算法之道>精华 算法设计部分 本书作者邹恒明,作者另有一本书<数据结构之弦>,以及<操作系统之哲学原理>都是非常好的书 这本书能够算得上是深入浅出.文笔非常好,作者加入了非常多自己的思考 本文仅包含算法设计部分,算法分析略去,并没有严格依照章节顺序来记录 附录 算法随想 有人喜欢遍历,希望踏遍千山万水,人生丰富多彩:有人一生贪婪,眼界不宽,及时行乐:有人注定穷搜,辛辛苦苦,收获有限:有人善用时空均衡,用最少的时间办最多的事情.十分精明:有人会分治,再难的问题也能解决.

《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——Alpha-Beta剪枝

2014.07.08 22:43 简介: “搜索”与“剪枝”几乎是如影随形的.此处的“搜索”指的是带有回溯算法的深度优先搜索. 在之前的“Minimax策略”中我们给出了一个三连棋的程序,运行后你就知道计算一步棋要花多少时间. 为了计算最优的一步棋,我们可能需要递归9万多次.如果毫无疑问这种阶乘式的穷举过程必须通过剪枝来加速. 本篇介绍一种用于Minimax策略的剪枝思路——α-β剪枝. 剪枝的英语是pruning,所以不要想当然说成trimming. 图示: 在上一篇讲解Minimax策略的博

【转载】数据结构与算法设计

原文: 数据结构与算法设计

算法设计之0-1背包问题

1.题目分析: 考虑到每种物品只有2 种选择,即装入背包或不装入背包,并且物品数和背包容量已给定,要计算装入背包物品的最大价值和最优装入方案,可用回溯法搜索子集树的算法进行求解. 2.算法设计:a. 物品有n种,背包容量为C,分别用p[i]和w[i]存储第i种物品的价值和重量,用x[i]标记第i种物品是否装入背包,用bestx[i]存储第i种物品的最优装载方案:b. 用递归函数Backtrack (i,cp,cw)来实现回溯法搜索子集树(形式参数i表示递归深度,n用来控制递归深度,形式参数cp和