妖怪与和尚过河问题
問題描述:
有三個和尚和三個妖怪,他們要利用唯一一條小船過河,這條小船一次最多只能載兩個人,同時,無論是在河的兩岸還是船上,只要妖怪的數量大于和尚的數量,妖怪們就會將和尚吃掉。現在需要選擇一種過河的安排,保證和尚和妖怪都能過河且和尚不能被妖怪吃掉。
問題分析:
將和尚和妖怪們的狀態抽象為一個數組
$status = array('left_missionary_num', //左岸和尚數量'left_monster_num', //左岸妖怪數量'right_missionary_num', //右岸和尚數量'right_monster_num' //右岸妖怪數量 )初始狀態為array(3,3,0,0),并且船在左岸,最終狀態是array(0,0,3,3),此時船在右岸。
船的左右移動會推動狀態的轉變,用-1表示船在左岸,用1表示船在右岸,當船靠岸時,船上的所有和尚或者妖怪要下船。
為了保證和尚不被妖怪吃掉,則需要滿足以下約束:
$status['left_missionary_num'] >= $status['left_monster_num'],表示左岸和尚的數量>=左岸妖怪的數量
$status['right_missionary_num'] >= $status['right_monster_num'],表示右岸和尚的數量>=右岸妖怪的數量
3 - $status['left_missionary_num'] - $status['right_missionary_num'] >= 3 - $status['left_monster_num'] - $status['right_monster_num'],表示船上的和尚數量>=船上的妖怪數量,因為船上最多只有2個位置,所以這個條件是默認滿足的。
狀態的搜索:
每一次狀態轉變時:
上船:要根據船的位置來判斷是左岸還是右岸的和尚或妖怪上船。
下船:要根據船的位置來判斷船上的和尚或妖怪下船后會加入左岸還是右岸。
下船后,要符合上面提到的約束。
并且上船后,要保證岸邊的和尚或者妖怪的數量都要 >= 0。
每個狀態可能會有多個下一個狀態,在不考慮約束的情況下,總共會有5種可能,分別是①1個和尚上船、②2個和尚上船、③1個妖怪上船、④2個妖怪上船,⑤一個和尚與一個妖怪上船。
對上述5種可能逐一進行約束驗證,如果驗證不通過,則忽略這種可能。
避免陷入"環路":
在搜索狀態時,會遇到“狀態環路”,例如array(3,3,0,0) -> array(3,2,0,1) -> array(3,3,0,0),有一些狀態環路包含的步驟可能會更多一些。如果對這種情況不做處理,搜索就會陷入循環從而無法得出結果。
如何避免“狀態環路”:
首先定義狀態的數據結構:
$status = array('id','value','ancestor_ids','boat_status' );$status包含4個屬性:id、value、ancestor_ids、boat_status,其中,每一個$status的id都是唯一的(id >= 0)(只有初始狀態的id = 0),value是一個數組,代表狀態的值,例如array(3,3,0,0),ancestor_ids也是一個數組,包含了(本狀態的)所有父、祖父、。。。、狀態的id,boat_status代表船的位置。
當一個狀態和它的ancestor_ids中包含的狀態中的任意一個的value和boat_status完全一致時,就可以判定這個狀態前面已經出現過了,直接忽略它就可避免“狀態環路”。
問題解答:
總共有4種不同的安排可以讓和尚和妖怪安全地過河,這4種安排所使用的步驟數目都是一樣的,都是11步。
程序設計(PHP):
1 <?php 2 echo '<p>問題描述:</p>'; 3 echo '<p>有三個和尚和三個妖怪,他們要利用唯一一條小船過河,這條小船一次最多只能載兩個人,同時,無論是在河的兩岸還是船上,只要妖怪的數量大于和尚的數量,妖怪們就會將和尚吃掉。現在需要選擇一種過河的安排,保證和尚和妖怪都能過河且和尚不能被妖怪吃掉。</p>'; 4 echo '<p>解答:</p>'; 5 echo '<p>其中,[]括號中數字的含義是:第一個代表左岸和尚的數量, 6 第二個代表左岸妖怪的數量,第三個代表右岸和尚的數量,第四個代表右岸妖怪的數量;boat status等于-1代表船在左岸,boat status等于1代表船在右岸。</p>'; 7 8 echo "<pre>"; 9 10 const left_missionary_num = 0; 11 const left_monster_num = 1; 12 const right_missionary_num = 2; 13 const right_monster_num = 3; 14 15 const boat_missionary_num = 0; 16 const boat_monster_num = 1; 17 18 $currentStatus = array( 19 left_missionary_num => 3, 20 left_monster_num => 3, 21 right_missionary_num=> 0, 22 right_monster_num => 0 23 ); 24 25 $boat = -1;//left bank 26 $statusId = 0; 27 $statuses = array( 28 0 => array( 29 'value' => $currentStatus, 30 'ancestor_ids' => array(), 31 'boat_status' => -1 32 ) 33 ); 34 35 findPath($currentStatus, $statusId, $statusId, $statuses, $boat); 36 37 echo '<p>搜索中產生的所有狀態(狀態樹):</p>'; 38 print_r($statuses); 39 40 41 42 function findPath(array $status, $id, &$statusId, &$statuses, $boat) 43 { 44 $availableBoatStatuses = array( 45 array(boat_missionary_num => 1, boat_monster_num => 0), 46 array(boat_missionary_num => 2, boat_monster_num => 0), 47 array(boat_missionary_num => 0, boat_monster_num => 1), 48 array(boat_missionary_num => 0, boat_monster_num => 2), 49 array(boat_missionary_num => 1, boat_monster_num => 1), 50 ); 51 foreach ($availableBoatStatuses as $availableBoatStatus) { 52 $nextStatus = generateNextStatus($status, $availableBoatStatus, $boat); 53 54 if($nextStatus){ 55 $_boat = -1 * $boat; 56 $ancestorIds = $statuses[$id]['ancestor_ids']; 57 $ancestorIds[] = $id; 58 59 $isAncestor = false; 60 foreach ($ancestorIds as $ancestorId) { 61 if($statuses[$ancestorId]['value'] === $nextStatus 62 && $_boat === $statuses[$ancestorId]['boat_status'] 63 ){ 64 $isAncestor = true; 65 break; 66 } 67 } 68 69 if($isAncestor){ 70 continue; //Ignore the result if $nextStatus === $statuses['$ancestorId'] 71 } 72 73 $statusId++; 74 75 $statuses[$statusId] = array( 76 'value' => $nextStatus, 77 'ancestor_ids' => $ancestorIds, 78 'boat_status' => $_boat 79 ); 80 81 if($nextStatus === array(0,0,3,3)){ 82 83 foreach ($statuses[$statusId]['ancestor_ids'] as $key => $ancestorId) { 84 //print_r($statuses[$ancestorId]['value']); 85 if($key % 5 == 0){ 86 echo '<br/>'; 87 } 88 $v = $statuses[$ancestorId]['value']; 89 printf('[%d,%d,%d,%d], boat status = %d -->', $v[0],$v[1],$v[2],$v[3],$statuses[$ancestorId]['boat_status']); 90 } 91 printf('[%d, %d, %d, %d], boat status = %d', $nextStatus[0],$nextStatus[1],$nextStatus[2],$nextStatus[3],$_boat); 92 echo '<br/>'; 93 94 printf('總共使用 %d 步完成', count($statuses[$statusId]['ancestor_ids'])); 95 echo '<hr>'; 96 }else{ 97 findPath($nextStatus, $statusId, $statusId, $statuses, $_boat); 98 } 99 } 100 } 101 } 102 103 /** 104 * @param array $status 105 * @param array $availableBoatStatus 106 * @param int $boatStatus 107 * @return array|bool 108 */ 109 function generateNextStatus(array $status, array $availableBoatStatus, $boatStatus) 110 { 111 if($boatStatus < 0){ 112 //船在左岸,上船后 113 $status[left_missionary_num] = $status[left_missionary_num] - $availableBoatStatus[boat_missionary_num]; 114 $status[left_monster_num] = $status[left_monster_num] - $availableBoatStatus[boat_monster_num]; 115 //船到右岸,下船后 116 $status[right_missionary_num] = $status[right_missionary_num] + $availableBoatStatus[boat_missionary_num]; 117 $status[right_monster_num] = $status[right_monster_num] + $availableBoatStatus[boat_monster_num]; 118 }else{ 119 //船在右岸,上船后 120 $status[right_missionary_num] = $status[right_missionary_num] - $availableBoatStatus[boat_missionary_num]; 121 $status[right_monster_num] = $status[right_monster_num] - $availableBoatStatus[boat_monster_num]; 122 //船到左岸,下船后 123 $status[left_missionary_num] = $status[left_missionary_num] + $availableBoatStatus[boat_missionary_num]; 124 $status[left_monster_num] = $status[left_monster_num] + $availableBoatStatus[boat_monster_num]; 125 } 126 127 foreach ($status as $value) { 128 if($value < 0){ 129 return false; 130 } 131 } 132 133 if(($status[left_missionary_num] >= $status[left_monster_num]) && 134 ($status[right_missionary_num] >= $status[right_monster_num]) 135 || $status[left_missionary_num] === 0 || $status[right_missionary_num] === 0){ 136 return $status; 137 } 138 139 return false; 140 }?
轉載于:https://www.cnblogs.com/jpdoutop/p/5775911.html
總結
- 上一篇: Minecraft 1.12.2模组开发
- 下一篇: 精简高效的CSS命名准则/方法