/**
*
* @author z84002131
* 很久很久以前,在一個森林裡,有獅子,老虎和狼,他們應大象的邀請,要到大象家做客。去的路上被一條小河攔住了,需要過河,只有一個獨木舟。現在已知:
* 1.總共有6隻,分別是獅子爸爸,小獅子,老虎爸爸,小老虎,狼爸爸和小狼。
* 2.小傢伙們都比較弱小,如果沒有爸爸罩著就會被旁邊的大傢伙吃掉。
* 3.獨木舟最多能坐2人,無視每隻的大小。
* 4.大傢伙都會划船,小傢伙裡只有小老虎會划船。
* 求:安全過河方法。
* 提示:可以設大獅子為A,小獅子為a,大老虎為B,小老虎為b,大狼為C,小狼為c。再形象的用豎線來表示河,0表示船,那麼題目就是要求畫出
* AaBbCc|0 |
* ...
* | 0|AaBbCc
* 中間被...省略的步驟
*/
import java.util.Vector;
public class AcrossTheRiver {
private static final int SHIP = 0;
private static final int LION_A = 1;
private static final int LION_a = 2;
private static final int TIGER_B = 3;
private static final int TIGER_b = 4;
private static final int WOLF_C = 5;
private static final int WOLF_c = 6;
private static final int TOTAL_STATUS = 7;
//用一個整型int來存放當前的狀態,第1位為0表示SHIP在左岸,第1位為1表示SHIP在右岸
// 第2位為0表示LION_A在左岸,第2位為1表示LION_A在右岸
// 第3位為0表示LION_a在左岸,第3位為1表示LION_a在右岸,往下以此類推
private static final int BEGIN_STATUS = 0;//初始狀態全都在左岸
private static final int END_STATUS = 0x7F;//目的是都到右岸去
private static final int SAILOR = (1 << LION_A) | (1 << TIGER_B) | (1 << TIGER_b) | (1 << WOLF_C);
private static Vector<Integer> stepVec = new Vector<Integer>();//存放擺渡的步驟,用於最後輸出步驟
//存放每個情境可能的下一步情境
//考慮到一共有6獸1船共7個物體,每個物體有左岸和右岸兩個狀態,因此一共有2的7次方共128個情境。
//每個情境的下一步,僅有數種可能的情境:如AaBb|0 |Cc的下一步中,僅有Aa| 0|BbCc和Bb| 0|AaCc這兩個合法的情境
//利用這張表,可以方便的預測出下一步可能的動作,從而進行推演
private static Vector<Integer>[] traceMap = createTraceMap();
//建立traceMap
private static Vector<Integer>[] createTraceMap(){
//建立情境表traceMap
Vector<Integer>[] traceMapTmp = new Vector[1 << TOTAL_STATUS];
//int iCount = 0;
for(int i = 0; i < (1 << TOTAL_STATUS); i++){
traceMapTmp[i] = new Vector<Integer>();
if(!isStatusLegal(i)){//如果目前狀態不合法就不用再看了
continue;
}
//printStatus(i);
int statusTmp;
if(isRight(i, SHIP)){//如果船在右岸
for(int j = 1; j < TOTAL_STATUS; j++){
if(isRight(i, j) && isSailor(j)){
//決定划船的
statusTmp = setLeft(setLeft(i, SHIP), j);
if(isStatusLegal(statusTmp)){
traceMapTmp[i].add(new Integer(statusTmp));
//iCount++;
//printStatus(statusTmp);
}
//決定是否帶乘客
int statusTmp2;
for(int k = 1; k < TOTAL_STATUS; k++){
if(isRight(statusTmp, k)){
statusTmp2 = setLeft(statusTmp, k);
if(isStatusLegal(statusTmp2)){
traceMapTmp[i].add(new Integer(statusTmp2));
//iCount++;
//printStatus(statusTmp2);
}
}
}
}
}
} else {//如果船在左岸
for(int j = 1; j < TOTAL_STATUS; j++){
if(!isRight(i, j) && isSailor(j)){
//決定划船的
statusTmp = setRight(setRight(i, SHIP), j);
if(isStatusLegal(statusTmp)){
traceMapTmp[i].add(new Integer(statusTmp));
//iCount++;
//printStatus(statusTmp);
}
//決定是否帶乘客
int statusTmp2;
for(int k = 1; k < TOTAL_STATUS; k++){
if(!isRight(statusTmp, k)){
statusTmp2 = setRight(statusTmp, k);
if(isStatusLegal(statusTmp2)){
traceMapTmp[i].add(new Integer(statusTmp2));
//iCount++;
//printStatus(statusTmp2);
}
}
}
}
}
}
//System.out.println("\n");
}
//System.out.println("iCount = " + iCount);
return traceMapTmp;
}//end of createTraceMap
//擺渡
private static boolean goTowards(int status){
//判斷是否勝利了
if(isSucceed(status)){
stepVec.add(new Integer(status));
return true;
}
if(stepVec.indexOf(status) == -1){//如果是之前沒遇過的情境,可以繼續走著看看
stepVec.add(new Integer(status));
}else{//如果轉回了之前的情境,那就放棄這種走法
return false;
}
for(int i = 0; i < traceMap[status].size(); i++){//窮舉每個可能的下一步情境繼續分析
if(goTowards(traceMap[status].elementAt(i).intValue())){//遞迴深入 窮舉
return true;
}
}
stepVec.remove(stepVec.size() - 1);//如果沒找到
return false;
}
//判斷勝利條件
private static boolean isSucceed(int status){
return (status == END_STATUS) ? true : false;
}
//判斷會不會划船的
private static boolean isSailor(int animal){
return (SAILOR & (1 << animal)) == (1 << animal);
}
//檢查在status狀態下,編號為animal的獸是否在右岸
private static boolean isRight(int status, int animal){
return (status & (1 << animal)) == (1 << animal);
}
//將某獸移動到左邊
private static int setLeft(int status, int animal){
return status & ((~0)^(1 << animal));
}
//將某獸移動到右邊
private static int setRight(int status, int animal){
return status | (1 << animal);
}
//判斷當前情況是否有小獸成為犧牲品
private static boolean isStatusLegal(int status){
for(int i = 1; i < TOTAL_STATUS; i += 2){
while(true){
//如果父子不在同岸
if(isRight(status, i) ^ isRight(status, i + 1)){
//那麼另外兩隻大獸必須和父親在同一邊
if((isRight(status, (i + 2) % 6) ^ isRight(status, i + 1))
&& (isRight(status, (i + 4) % 6) ^ isRight(status, i + 1))){
break;
}
//否則小獸就掛了
return false;
//如果父子同岸,那麼有大獸保護,小獸沒問題
}else {
break;
}
}
}
return true;
}
//列印目前狀態
private static void printStatus(int status){
StringBuffer strTmp = new StringBuffer("AaBbCc|0 0|AaBbCc");
if(isRight(status, SHIP)){
strTmp.replace(7, 8, " ");
} else {
strTmp.replace(10, 11, " ");
}
for(int i = 1; i < TOTAL_STATUS; i++){
if(isRight(status, i)){
strTmp.replace(i - 1, i, " ");
} else {
strTmp.replace(i + 11, i + 12, " ");
}
}
System.out.println(strTmp);
}
//列印出步驟結果
private static void showStep(){
goTowards(BEGIN_STATUS);
for(int i = 0; i < stepVec.size(); i++){
printStatus(stepVec.elementAt(i).intValue());
}
}
public static void main(String[] args) {
showStep();
}
}