1. 程式人生 > >農夫、狼、羊、白菜(回溯法求解)

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

         為了複試準備,最近做了很多上機題,映象最最深的就是一道關於農夫、狼、羊、白菜的問題。該問題描述如下:有一個農夫帶一隻羊、一筐菜和一隻狼過河.果沒有農夫看管,則狼要吃羊,羊要吃菜.但是船很小,只夠農夫帶一樣東西過河。問農夫該如何解此難題?

        開始拿到這題的時候,毫無思路,就一直放在那裡。後來,看《計算機演算法機基礎》回溯法時,直覺可以用它來解決問題。受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;
}