上周被人出了一道算法題目,題目的內容是:有10個小球,外觀一樣,其中1個小球和其他9個小球重量不一樣,請使用天平以最少的次數找出這個重量不一樣的小球。
解題思路:其實這是一個很典型使用分治算法的例子,由於不知道這個特別的小球是比其他球重還是輕,所以我們不能簡單的使用二分法去求解,所以我選擇3為模。
解題步驟:
1、將10個球分成4個數組,分別是A[a1, a2, a3],B[b1, b2, b3], C[c1, c2, c3], D[d1];假設這個重量不一樣的球為n,其他普通球的任意一個是m;
2、首先拿A和B、A和C稱,如果A==B && A== C,那么直接得到結果n = d1;否則我們可以獲得兩個結果,第一個n in [A|B|C],第二個是n比m重還是輕。
說明:
如果A > B && A > C, 那么n in A,n > m
如果A > B && A == C,那么n in B,n < m
如果A > B && A < C, 這種情況不存在
如果A == B && A > C,那么n in C, n < m
如果A == B && A == C,這種情況我們已經得到結果
如果A == B && A < C,那么n in C, n > m
如果A < B && A < C, 那么n in A,n < m
如果A < B && A == C, 那么n in B,n > m
如果A < B && A > C,這種情況不存在
3、我們得到n in [A|B|C]后,取A、B、C中的前兩個球稱,因為已經知道n比m重還是輕,所以稱完后可以直接得到n。假設n in A
說明:
如果a1 > a2 && n > m, 那么n = a1
如果a1 > a2 && n < m, 那么n = a2
如果a1 == a2 ,那么n = a3
如果a1 < a2 && n > m,那么n = a2
如果a1 < a2 && n < m,那么n = a1
上代碼:
<?php $data = array(0, 0, 0, 0, 1, 0, 0, 0, 0, 0); class filterBall { public $theSpecial; //特殊球 1 = 重, -1 = 輕 public $balanceTimes = 0; /* * param integer & array $left 左邊的球 * param integer & array $right 右邊的球 * return 左邊的球重=-1 相同重=0 右邊球重=1 */ public function balance($left, $right) { $this->balanceTimes++; if (is_array($left)) { $left = array_sum($left); } if (is_array($right)) { $right = array_sum($right); } if ($left == $right) { return 0; } elseif ($left > $right) { return -1; } else { return 1; } } function execute($data) { $mod = intval(count($data) / 3); //取模 // 將數組切分成3 - 4個每個有$mod個元素的數組 $i = 0; $j = 0; $newArray = array(); while($i < count($data)) { if (isset($newArray[$j]) && count($newArray[$j]) >= $mod) { $j++; } $newArray[$j][] = $data[$i]; $i++; } $return0 = $this->balance($newArray[0], $newArray[1]); $return1 = $this->balance($newArray[0], $newArray[2]); switch((string)$return0.(string)$return1) { case '11': $this->theSpecial = -1; return 0 + $this->compareThird($newArray[0]); case '10': $this->theSpecial = 1; return 3 + $this->compareThird($newArray[1]); case '01': $this->theSpecial = 1; return 6 + $this->compareThird($newArray[2]); case '00': return 9; case '0-1': $this->theSpecial = -1; return 6 + $this->compareThird($newArray[2]); case '-10': $this->theSpecial = -1; return 3 + $this->compareThird($newArray[1]); case '-1-1': $this->theSpecial = 1; return 0 + $this->compareThird($newArray[0]); } } /* * 通過前兩次的比對,知道特別球是重還是輕,然后在三個球中比對一次,知道三個球中特殊球的索引 */ public function compareThird($childArray) { $return = $this->balance($childArray[0], $childArray[1]); switch($return) { case 1: case -1: if ($return == $this->theSpecial) { return 1; } else { return 0; } break; case 0: return 2; break; } } } echo '<pre>'; print_r($data); $filter = new filterBall(); echo sprintf('鍵值:%s, 比對了%s次', $filter->execute($data), $filter->balanceTimes);