农夫、狼、羊、白菜(回溯法求解)

         为了复试准备,最近做了很多上机题,映象最最深的就是一道关于农夫、狼、羊、白菜的问题。该问题描述如下:有一个农夫带一只羊、一筐菜和一只狼过河.果没有农夫看管,则狼要吃羊,羊要吃菜.但是船很小,只够农夫带一样东西过河。问农夫该如何解此难题?

        开始拿到这题的时候,毫无思路,就一直放在那里。后来,看《计算机算法机基础》回溯法时,直觉可以用它来解决问题。受8-皇后算法启发,终于解决了该问题。代码如下:

/*
	回溯法解决农夫、狼、羊、白菜问题,
	其中将农夫每一次过河称为一次操作
*/
#include <stdio.h>

/*设置经过的最多步骤*/
enum { MAX_STEP=15};

/* 每种操作所对应的文字描述*/
char *chop[8]={
	"sheep_go","sheep_come","vegetable_go","vegetable_come",
		"wolf_go","wolf_come","nothing_go","nothing_come"
};

int isFinal(int iStep);
int isFeasible(int iStep,int i) ;
int islegal(int *p);
void print(int iStep);

/*状态数组,最多15步*/
int ans[MAX_STEP][4]={0};

/*操作记录数组,每个步骤所对应的操作*/
int solve[MAX_STEP]={0};

int main()
{
	int i;
	int iStep;
	iStep=1;
	solve[1]=0;
	/*
		初始时农夫、狼、羊、白菜均未过河,记录其状态为{1,1,1,1}
	则他们过河完成以后其状态为{0,0,0,0}
	*/
	for(i=0;i<4;++i) ans[0][i]=1;   

	while(iStep>0)    
	{
		++solve[iStep];    //变为下一个操作
		while((solve[iStep]<=8)&&(!isFeasible(iStep,solve[iStep]))) //检测该操作是否可行
			++solve[iStep];

		if(solve[iStep]<=8)  //如果操作可行
		{

			if(isFinal(iStep)) {print(iStep);}   //若已经完成,则打印结果
			else
			{
				++iStep;           //进行下一步
			   solve[iStep]=0;     //初始时无操作
			}
		}
		else --iStep;    //遍历所有的操作皆不可行则回溯到上一步
	}//end while
	return 0;
}

/*
	根据操作数组solve中的操作记录
	依次显示操作过程
*/
void print(int iStep)
{
	int i;
	for(i=1;i<=iStep;++i)
	{
		printf("%s\n",chop[solve[i]-1]); //solve[i]代表第i步所对应的操作
	}
	printf("succeed\n\n");
}

/*
	检测是否到达结束状态
*/
int isFinal(int iStep)
{
	if((ans[iStep][0]+ans[iStep][1]+ans[iStep][2]+ans[iStep][3])==0) return 1; //对应状态为全0则结束
	else return 0;
}

/*
	检测在第iStep步使用操作i是否可行
*/
int isFeasible(int iStep,int i)
{
	int chAns[4];    //用于copy前一个步骤的状态
	int j,k;         //用于遍历数组 

	for(k=0;k<4;++k) chAns[k]=ans[iStep-1][k];  //copy

	switch(i)      //操作i所对应的状态改变
	{
	case 1:if(chAns[0]==0||chAns[2]==0)  return 0;   //sheep_go
		else chAns[0]=chAns[2]=0;
		break;
	case 2:if(chAns[0]==1||chAns[2]==1)  return 0;   //sheep_come
		else chAns[0]=chAns[2]=1;
		break;
	case 3:if(chAns[0]==0||chAns[3]==0)  return 0;	 //vegetable_go
		else chAns[0]=chAns[3]=0;
		break;
    case 4:if(chAns[0]==1||chAns[3]==1)  return 0;   //vegetable_come
		else chAns[0]=chAns[3]=1;
		break;
	case 5:if(chAns[0]==0||chAns[1]==0)  return 0;   //wolf_go
		else chAns[0]=chAns[1]=0;
		break;
	case 6:if(chAns[0]==1||chAns[1]==1)  return 0;   //wolf_come
		else chAns[0]=chAns[1]=1;
		break;
	case 7:if(chAns[0]==0)  return 0;               //nothing_go
		else chAns[0]=0;
		break;
	case 8:if(chAns[0]==1)  return 0;              //nothing_come
		else chAns[0]=1;
		break;
	}

	/*
		当前状态不能与之前的状态相同,
		否则陷入无限重复情况
	*/
	for(j=0;j<iStep;++j)
	{
		for(k=0;k<4;++k)
			if(ans[j][k]!=chAns[k]) break;
		if(k>=4)  return 0;                 //与之前状态相同
	}

	if(!islegal(chAns)) return 0;           //检查chAns所对应的状态是否是合法状态
	else { //当前状态是可行的
		for(k=0;k<4;++k) ans[iStep][k]=chAns[k];  //copy
		solve[iStep]=i;      //记录到操作记录数组
		return 1;
	}
}

/*
检查数组p所对应的状态是否合法,
即是否存在狼吃羊或羊吃白菜的情况
*/
int islegal(int *p)
{
	if((p[1]==p[2])&&(p[1]!=p[0])) return 0;
	if((p[2]==p[3])&&(p[2]!=p[0])) return 0;
	return 1;
}

    原文作者:回溯法
    原文地址: https://blog.csdn.net/timepe/article/details/7425272
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞