每週一道算法題003:翻牌

問題:

有100張寫着數字1~100的牌,並按順序排列。最開始所有牌都是背面朝上放置。某人從第2張牌開始,隔1張牌翻牌。然後第2, 4, 6, …,100張牌就會變成正面朝上。接下來,另一個人從第3張牌開始,隔2張牌翻牌(原本背面朝上的,翻轉成正面朝上;原本正面朝上的,翻轉成背面朝上)。再接下來,又有一個人從第4張牌開始,隔3張牌翻牌。像這樣,從第n張牌開始,每隔n-1張牌翻牌,直到沒有可翻動的牌爲止。
每週一道算法題003:翻牌
求當所有牌不再變動時,所有背面朝上的牌的數字。

思路:

這道題思路有很多種:

思路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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章