農夫、狼、羊、白菜(回溯法求解)
阿新 • • 發佈:2019-02-02
為了複試準備,最近做了很多上機題,映象最最深的就是一道關於農夫、狼、羊、白菜的問題。該問題描述如下:有一個農夫帶一隻羊、一筐菜和一隻狼過河.果沒有農夫看管,則狼要吃羊,羊要吃菜.但是船很小,只夠農夫帶一樣東西過河。問農夫該如何解此難題?
開始拿到這題的時候,毫無思路,就一直放在那裡。後來,看《計算機演算法機基礎》回溯法時,直覺可以用它來解決問題。受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; }