上周被人出了一道算法题目,题目的内容是:有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);