問題:
有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 main
import "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 []int
for i := 1; i <= size; i++ {
flag := false
for 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 []int
for 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]