一个农夫带着一只狼、一只羊和一棵白菜,身处河的南岸。他要把这些东西全部运到北岸。遗憾的是他只有一只小船,小船只能容下他和一件物品。这里只能是农夫来撑船。同时因为狼吃羊,而羊吃白菜,所以农夫不能留下羊和狼或者羊和白菜单独在河的一边,自己离开。好在狼属于肉食动物,不吃白菜。农夫怎样才能把所有的东西安全运过河呢?
为了表示每个物品的位置,采用二进制位来区分南岸和北岸,0表示在南岸,1表示在北岸。用四个二进制位XXXX分别表示农夫、狼、菜和羊四个物品所在的位置。例如1110表示农夫、狼和菜在北岸,菜在南岸。农夫过河问题的初始状态为0000,结束状态为1111。

输入格式:

无输入

输出格式:

输出农夫移动物品的中间状态的逆序

输入样例:

输出样例:

15 6 14 2 11 1 9 0

代码

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
struct Queue
{
    int Max;
    int f;
    int r;
    DataType *elem;
};
typedef struct Queue *SeqQueue;

SeqQueue SetNullQueue_seq(int m)
{
    SeqQueue squeue;
    squeue = (SeqQueue)malloc(sizeof(struct Queue));
    if (squeue == NULL)
    {
        printf("Alloc failure\n");
        return NULL;
    }
    squeue->elem = (int *)malloc(sizeof(DataType) * m);
    if (squeue->elem != NULL)
    {
        squeue->Max = m;
        squeue->f = 0;
        squeue->r = 0;
        return squeue;
    }
}

int IsNullQueue_seq(SeqQueue squeue)
{
    return (squeue->f == squeue->r);
}
void EnQueue_seq(SeqQueue squeue, DataType x)
{
    if ((squeue->r + 1) % squeue->Max == squeue->f)
        printf("It is FULL Queue!");
    else
    {
        squeue->elem[squeue->r] = x;
        squeue->r = (squeue->r + 1) % squeue->Max;
    }
}
void DeQueue_seq(SeqQueue squeue)
{
    if (IsNullQueue_seq(squeue) == 1)
        printf("it is empty queue!");
    else
    {
        squeue->f = (squeue->f + 1) % (squeue->Max);
    }
}
DataType FrontQueue_seq(SeqQueue squeue)
{
    if (IsNullQueue_seq(squeue) == 1)
    {
        printf("It is empty queue");
        return 33;
    }
    else
    {
        return squeue->elem[squeue->f];
    }
}
//***************************************
int FarmerOnRight(int status) //判断当前条件下农夫的位置
{
    return (0 != (status & 0x08));
}
int WorfOnRight(int status) //判断当前位置下狼的位置
{
    return (0 != (status & 0x04));
}
int CabbageOnRight(int status) //判断当前位置下白菜的位置
{
    return (0 != (status & 0x02));
}
int GoatOnRight(int status) //判断当前位置下羊是否在南岸
{
    return (0 != (status & 0x01));
}
int IsSafe(int status) //判断当前位置下羊是否安全
{
    if ((GoatOnRight(status) == CabbageOnRight(status)) && (GoatOnRight(status) != FarmerOnRight(status)))
        return 0; //羊吃白菜
    if ((GoatOnRight(status) == WorfOnRight(status)) && (GoatOnRight(status) != FarmerOnRight(status)))
        return 0; //狼吃羊
    return 1;     //其他状态时安全的
}
void FarmerRiver()
{
    int i, movers, nowstatus, newstatus;
    int status[16];                //用于记录已考虑的状态路径
    SeqQueue moveTo;               //用于记录可以安全到达的中间状态
    moveTo = SetNullQueue_seq(20); //创建空队列
    EnQueue_seq(moveTo, 0x00);     //初始状态时所有物品在南岸
    for (i = 0; i < 16; i++)       //数组status初始化位-1
        status[i] = -1;
    status[0] = 0;
    while (!IsNullQueue_seq(moveTo) && (status[15] == -1))
    //队列非空且没有到达结束状态
    {
        nowstatus = FrontQueue_seq(moveTo); //取队头状态为当前状态
        DeQueue_seq(moveTo);
        for (movers = 1; movers <= 8; movers <<= 1) //遍历三个要移动的物品
                                                    //考虑各种物品移动
            if ((0 != (nowstatus & 0x08)) == (0 != (nowstatus & movers)))
            //考虑农夫与移动的物品在同一侧
            {
                newstatus = nowstatus ^ (0x08 | movers); //计算新状态
                //如果新状态时安全的且之前没有出现过
                if (IsSafe(newstatus) && (status[newstatus] == -1))
                {
                    status[newstatus] = nowstatus;  //记录新状态
                    EnQueue_seq(moveTo, newstatus); //新状态入队
                }
            }
    }
    //输出经过的装态路径
    if (status[15] != -1) //到达最终状态
    {
        // printf("the reverse path is:\n");
        for (nowstatus = 15; nowstatus >= 0; nowstatus = status[nowstatus])
        {
            // printf("The nowstatus is:%d\n", nowstatus);
            printf("%d ", nowstatus);
            if (nowstatus == 0)
                return;
        }
    }
    else
        printf("No solution.\n"); //问题无解
}
int main()
{
    FarmerRiver();
    return 0;
}