每周一道算法题003:翻牌
问题:
有100张写着数字1~100的牌,并按顺序排列。最开始所有牌都是背面朝上放置。某人从第2张牌开始,隔1张牌翻牌。然后第2, 4, 6, …,100张牌就会变成正面朝上。接下来,另一个人从第3张牌开始,隔2张牌翻牌(原本背面朝上的,翻转成正面朝上;原本正面朝上的,翻转成背面朝上)。再接下来,又有一个人从第4张牌开始,隔3张牌翻牌。像这样,从第n张牌开始,每隔n-1张牌翻牌,直到没有可翻动的牌为止。
求当所有牌不再变动时,所有背面朝上的牌的数字。
思路:
这道题思路有很多种:
思路1
设i为轮次,从1开始,到99结束。设j为牌的下标。
每一轮都按题意找到对应下标的牌逐个进行翻转。
思路2
设i为轮次,从1开始,到99结束;设j为牌的下标。
每一轮都对符合要求的所有牌进行翻转。
思路3
牌只有在偶数次翻转时才会背面朝上。
而牌翻转的时机是:步长为该牌面的约数时,这一点不太好理解,举例说明。
如:4号牌,初始是背面朝上,
第1轮,从2号牌开始,步长为2,4号牌被翻动,正面朝上;
第2轮,从3号牌开始,步长为3,4号牌不被翻动;
第3轮,从4号牌开始,步长为4,4号牌被翻动,背面朝上;
以后不会再被翻动
可以看到,当步长为2,4时,4号牌都会发生翻动,而2,4都是4的约数。
如:6号牌,初始是背面朝上,
第1轮,从2号牌开始,步长为2,6号牌被翻动,正面朝上;
第2轮,从3号牌开始,步长为3,6号牌被翻动,背面朝上;
第3轮,从4号牌开始,步长为4,6号牌不被翻动;
第4轮,从5号牌开始,步长为5,6号牌不被翻动;
第5轮,从6号牌开始,步长为6,6号牌被翻动,正面朝上;
以后不会再被翻动
可以看到,当步长为2,3,6时,6号牌都会发生翻动,而2,3,6都是6的约数。
如果算上1这个所有自然数的约数,那只要约数的个数是奇数个,最终都会背面朝上。
还可以继续归纳:
比如:
12号牌,约数为1,2,3,4,6,12,共有6个约数,最终是正面朝上;
16号牌,约数为1,2,4,8,16,共用有5个约数,最终是背面朝上;
25号牌,约数为1,5,25,共有3个约数,最终是背面朝上;
注意到,所有背面朝上的牌4,16,25,它们都是平方数。
所以这道题最终就变成了找出1-100中所有的平方数。
解答:
以下按上述三条思路分别给出PHP和Golang的代码
PHP
// 按顺序进行翻牌,i为牌下标,j为牌下标+步长
function flip1()
{$size = 100;$cards = array_fill(0, $size, 0); // 初始化数组// i为轮次for ($i = 1; $i <= $size; $i++) {// 如果当前的牌是正面,就翻过来;反之亦然。// 每轮步长增长为i+1,// 例如:// 第一轮起始下标是1,步长是2(=1+1),翻1,3,5...下标的牌// 第二轮起始下标是2,步长是3(=2+1),翻2,5,8...下标的牌// 以此类推for ($j = $i; $j < $size; $j += $i + 1) {$cards[$j] = !$cards[$j];}}output($cards);
}// i为轮次,j为牌下标
// 第1轮:2,4,6...100的牌被翻转,对应的下标为1,2,3...99,(j+1)%(1+1)==0
// 第2轮:3, 6, 9...99的牌被翻转,对应的下标为2,5,8...98,(j+1)%(2+1)==0
// 以此类推,得到公式,(j+1)%(i+1)==0时,牌都会翻转
function flip2()
{$size = 100;$cards = array_fill(0, $size, 0); // 初始化数组for ($i = 1; $i < $size; $i++) {for ($j = 1; $j < $size; $j++) {if (($j + 1) % ($i + 1) == 0) {$cards[$j] = !$cards[$j];}}}output($cards);
}// 当牌i翻转为偶数次时,即为背面朝上
// 当j为i的约数时,会触发一次i的翻转
// 比如:牌4,会在约数为1,2,4时被翻转
// 但所有的牌都是从约数为2开始翻,所以排除掉约数1的情况
// 此时,4号牌只翻转了2次,符合偶数次翻转的情况,所以其最终是背面朝上
function flip3()
{$size = 100;$tmp = array();// i为牌面,数字为1-100的100张牌for ($i = 1; $i <= $size; $i++) {$flag = false;// j为步长for ($j = 2; $j <= $size; $j++) {if ($i % $j == 0) {$flag = !$flag;}}if ($flag == false) {$tmp[] = $i;}}echo implode(' ', $tmp) . "\n";
}function output($cards)
{foreach ($cards as $key => $val) {if (!$val) {echo $key + 1;echo " ";}}echo "\n";
}flip1();
flip2();
flip3(); 输出
1 4 9 16 25 36 49 64 81 100
1 4 9 16 25 36 49 64 81 100
1 4 9 16 25 36 49 64 81 100 Golang
package mainimport "fmt"var size = 100 // 牌数func main() {Flip1()Flip2()Flip3()
}// 初始化数据
func initCards() []bool {var cards []bool // 存放每张牌的状态for i := 0; i < size; i++ {cards = append(cards, false)}return cards
}// 翻牌算法1
func Flip1() {cards := initCards()for i := 1; i < size; i++ {for j := i; j < size; j += i + 1 {cards[j] = !cards[j]}}PrintCards(cards)
}// 翻牌算法2
func Flip2() {cards := initCards()for i := 1; i < size; i++ {for j := i; j < size; j++ {if (j+1)%(i+1) == 0 {cards[j] = !cards[j]}}}PrintCards(cards)
}// 翻牌算法3
func Flip3() {var cards []intfor i := 1; i <= size; i++ {flag := falsefor j := 2; j <= size; j++ {if i%j == 0 {flag = !flag}}if flag == false {cards = append(cards, i)}}fmt.Println(cards)
}// 输出牌面
func PrintCards(cards []bool) {var results []intfor i := 0; i < size; i++ {if cards[i] == false {results = append(results, i+1)}}fmt.Println(results)
} 输出
[1 4 9 16 25 36 49 64 81 100]
[1 4 9 16 25 36 49 64 81 100]
[1 4 9 16 25 36 49 64 81 100]
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
