7-1 农夫过河-广度策略

7-1 农夫过河-广度策略 (100 分)

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

一.基本思路:
1.初始状态0000入队
2.当队列不空且没有到达结束状态1111时循坏以下操作:
①队头状态出队
②按照农夫一个人走、农夫分别带上三个走循坏以下操作:
~如果农夫和他们在同一岸,则计算新的状态。
~如果新状态是安全的并且是没有处理过的,则更新status[],并将新状态入队。
③当状态为1111时逆向输出status[]数组。
二.需要注意的时状态能否入队,要判断以下3条,满足一条件均不能入队。
①不可能:通过判断是否在统一对岸。
②不安全:通过安全函数判断。
③处理过:记录处理过的状态。

代码及注释如下

#include <stdio.h>
#include <stdlib.h>
typedef char 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);
            if(nowstatus==0)
                return;
        }

    }
    else
        printf("No solution.\n");//问题无解

}
int main()
{
    FarmerRiver();
    return 0;
}