文章目录
第1章 经典算法题
1.1 有多少苹果用来分赃
题目描述:
有5个人偷了一堆苹果,他们准备在第二天进行分赃。晚上,有个人溜出来,把苹果分成5份,将多余的一个扔给了树上的猴子,自己先将1/5藏了起来。其他4个人也都像第一个人那样,将苹果分成5份,将多余的一个扔给树上的猴子,偷走了1/5。第二天,大家分赃,也是分成5份,将多余的一个扔给猴子,最后一人分了一份。问:共有多少个苹果?
分析与解答:
假设总的苹果数量为s,上一个人对苹果划分时剩余的苹果数量为y,则有y=s-s/5-1。从这个公式开始,第一个人分的苹果总数s为最初的苹果总数,第二个人开始分赃直到结束分赃时,这个s都为上一个人分完剩余的苹果数量。以此类推。
<?php
/**
* 1.有多少苹果用来分赃
* 5人偷了一堆苹果,准备第二天分赃。晚上,一人偷偷将苹果分成5份,多的一个给了猴,自己藏了1/5。
* 剩下的其他4个人,也同样这么想的,都像第一个人那样,将剩下的苹果分成5份,多的一个给猴,藏1/5。
* 最后一人分了一份。问:苹果几何?
*/
for($s = 5; ; $s++) {
if($s % 5 == 1 ) {
$m = $s - round($s/5) - 1; // round()函数对浮点数进行四舍五入
if($m % 5 == 1) {
$n = $m - round($m/5) - 1;
if($n % 5 == 1) {
$k = $n - round($n/5) - 1;
if($k % 5 == 1) {
$x = $k - round($k/5) - 1;
if($x % 5 == 1) {
$y = $x - round($x/5) - 1;
if($y % 5 == 1) {
echo $s;
exit();
}
}
}
}
}
}
}
?>
// 15621
1.2 选猴王
题目描述:
群猴排成圈,按1~n依次编号。从第1只开始数,数到第m只,第m只猴出圈。从第m+1只开始,数到第m只,该猴出圈。以此类推,最后剩下一只猴,即可成为新任猴王。编程模拟此过程,输入m、n,输出猴王编号。
分析与解答:
首先将猴从1~n编号存放到数组中,对猴子的总个数进行循环,循环时将数到编号的猴子从数组删除,将未数到的猴子从原位置移到数组末尾,移动后需将原位置的编号删除。只要判断该编号数组个数大于1都继续循环,直到数组最后只剩下一个编号,这个编号则为猴王。
<?php
header("Content-type:text/html;charset=utf-8");
function monkeyKing($n, $m) {
$monkeys = range(1, $n);
$i = 0;
$k = $n;
while(count($monkeys) > 1) {
if(($i+1) % $m == 0) {
unset($monkeys[$i]);
} else {
array_push($monkeys, $monkeys[$i]);
unset($monkeys[$i]);
}
$i++;
}
return current($monkeys);
}
$monkey = monkeyKing(5, 2);
echo "最后当猴王的猴子编号为:",$monkey; // 最后当猴王的猴子编号为:3
?>
1.3 移动多少盘子才能完成汉诺塔游戏(递归)
题目描述:
汉诺塔游戏规则如下:
1)有三根柱子A、B、C,A柱上有若干盘子;
2)每次移动一只盘子,只能把小的盘子叠在大的盘子上面;
3)把所有盘子从A柱移到C柱上;
4)经研究发现,汉诺塔的破解很简单,就是按照移动规则向一个方向移动盘子。
5)例如3阶汉诺塔的移动:A→C,A→B,C→B,A→C,B→A,B→C,A→C。共移动7次。
实现代码:
<?php
function hanou($n, $x, $y, $z) {
if($n == 1) {
echo '移动盘子 1 从 '.$x. ' 到 '.$z."\n";
} else {
hanou($n-1, $x, $z, $y);
echo "移动盘子 $n 从 $x 到 $z\n";
hanou($n-1, $y, $x, $z);
}
}
hanou(3, 'A', 'B', 'C');
?>
控制台输出:
移动盘子 1 从 A 到 C
移动盘子 2 从 A 到 B
移动盘子 1 从 C 到 B
移动盘子 3 从 A 到 C
移动盘子 1 从 B 到 A
移动盘子 2 从 B 到 C
移动盘子 1 从 A 到 C
1.4 约瑟夫环
题目描述:
Josephus和他的朋友及另外39个犹太人共41个人排成一圈,从第一个人开始报数,每报道第三个人该人就必须自杀,然后由下一个人从1重新开始报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵守规则,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
分析与解答:
使用程序来求解的话,只要将阵列当作环状来处理,在阵列中由计数1开始,每3个数得到一个计数,直到计数到41为止;然后将阵列由索引1开始列出,就可以得知每个位置的自杀顺序,这就是约瑟夫排列。41个人报数的约瑟夫排列(自杀顺序)如下表所示:
排列顺序 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
自杀顺序 | 14 | 36 | 1 | 38 | 15 | 2 | 24 | 30 | 3 | 16 |
排列顺序 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
自杀顺序 | 34 | 4 | 25 | 17 | 5 | 40 | 31 | 6 | 18 | 26 |
排列顺序 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
自杀顺序 | 7 | 37 | 19 | 8 | 35 | 27 | 9 | 20 | 32 | 10 |
排列顺序 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
自杀顺序 | 41 | 21 | 11 | 28 | 39 | 12 | 22 | 33 | 13 | 29 |
排列顺序 | 41 | |||||||||
自杀顺序 | 23 |
实现代码:
<?php
header("content-type:text/html;charset=utf-8");
define("N",41); // 参与总人数
define("M",3); // 每到3自杀一人
$man = array(0);
$count = 1;
$i = 0;
$pos = -1;
$alive = 2; // 想救的人数
while($count <= N) {
do{
$pos = ($pos+1)%N; // 环状处理
if(@$man[$pos] == 0) {
$i++;
}
if($i == M) {
$i = 0; // $i重置
break;
}
} while(1);
$man[$pos] = $count;
$count++;
}
// 截取出对应最大键值对应的键名
arsort($man);
$arr = array_slice($man, 0, $alive, true