2022-07-15:一開始有21個球,甲和乙輪流拿球,甲先、乙後, 每個人在自己的回合,一定要拿不超過3個球,不能不拿

moonfdd發表於2022-07-15

2022-07-15:一開始有21個球,甲和乙輪流拿球,甲先、乙後,
每個人在自己的回合,一定要拿不超過3個球,不能不拿。
最終誰的總球數為偶數,誰贏。
請問誰有必勝策略。
來自微眾。人工智慧崗。

答案2022-07-15:

21球,是甲贏。如果把21變成其他正整數,誤以為甲也一定贏,但實際上是不一定。可能甲贏,乙贏,打平。
透過遞迴可以發現規律。1甲 3乙 5乙 7乙,9甲 11乙 13乙 15乙。

程式碼用rust編寫。程式碼如下:

fn main() {
    let mut i = 1;
    while i < 500 {
        //let ans = win1(i,0,0,0);
        let ans = win(i);
        println!("i = {},ans = {}", i, ans);
        i += 2;
    }
}

// balls = 21
// ball是奇數
fn win(balls: i32) -> String {
    return process(0, balls, 0, 0);
}
// 憋遞迴!
// turn 誰的回合!
// turn == 0 甲回合
// turn == 1 乙回合
// rest剩餘球的數量
// 之前,jiaBalls、yiBalls告訴你!
// 當前,根據turn,知道是誰的回合!
// 當前,還剩多少球,rest
// 返回:誰會贏!
fn process(turn: i32, rest: i32, jia: i32, yi: i32) -> String {
    if rest == 0 {
        return if (jia & 1) == 0 {
            String::from("甲")
        } else {
            String::from("乙")
        };
    }
    // rest > 0, 還剩下球!
    if turn == 0 {
        // 甲的回合!
        // 甲,自己贏!甲贏!
        for pick in 1..=get_min(rest, 3) {
            // pick 甲當前做的選擇
            if process(1, rest - pick, jia + pick, yi) == "甲" {
                return String::from("甲");
            }
        }
        return String::from("乙");
    } else {
        for pick in 1..=get_min(rest, 3) {
            // pick 甲當前做的選擇
            if process(0, rest - pick, jia, yi + pick) == ("乙") {
                return String::from("乙");
            }
        }
        return String::from("甲");
    }
}

fn get_min<T: Clone + Copy + std::cmp::PartialOrd>(a: T, b: T) -> T {
    if a < b {
        a
    } else {
        b
    }
}

// 我們補充一下設定,假設一開始的球數量不是21,是任意的正數
// 如果最終兩個人拿的都是偶數,認為無人獲勝,平局
// 如果最終兩個人拿的都是奇數,認為無人獲勝,平局
// rest代表目前剩下多少球
// cur == 0 代表目前是甲行動
// cur == 1 代表目前是乙行動
// first == 0 代表目前甲所選的球數,加起來是偶數
// first == 1 代表目前甲所選的球數,加起來是奇數
// second == 0 代表目前乙所選的球數,加起來是偶數
// second == 1 代表目前乙所選的球數,加起來是奇數
// 返回選完了rest個球,誰會贏,只會返回"甲"、"乙"、"平"
// win1方法,就是徹底暴力的做所有嘗試,並且返回最終的勝利者
// 在甲的回合,甲會嘗試所有的可能,以保證自己會贏,如果自己怎麼都不會贏,那也要儘量平局,如果這個也不行,只能對方贏
// 在乙的回合,乙會嘗試所有的可能,以保證自己會贏,如果自己怎麼都不會贏,那也要儘量平局,如果這個也不行,只能對方贏
// 演算法和資料結構體系學習班,影片39章節,牛羊吃草問題,就是類似這種遞迴
fn win1(rest: i32, cur: i32, first: i32, second: i32) -> String {
    if rest == 0 {
        if first == 0 && second == 1 {
            return String::from("甲");
        }
        if first == 1 && second == 0 {
            return String::from("乙");
        }
        return String::from("平");
    }
    if cur == 0 {
        // 甲行動
        let mut best_ans = String::from("乙");
        for pick in 1..=get_min(3, rest) {
            let mut cur_ans = win1(rest - pick, 1, first ^ (pick & 1), second);
            if cur_ans == "甲" {
                best_ans = String::from("甲");
                break;
            }
            if cur_ans == "平" {
                best_ans = String::from("平");
            }
        }
        return best_ans;
    } else {
        // 乙行動
        let mut best_ans = String::from("甲");
        for pick in 1..=get_min(3, rest) {
            let mut cur_ans = win1(rest - pick, 0, first, second ^ (pick & 1));
            if cur_ans == "乙" {
                best_ans = String::from("乙");
                break;
            }
            if cur_ans == "平" {
                best_ans = String::from("平");
            }
        }
        return best_ans;
    }
}

執行結果如下:

在這裡插入圖片描述


左神java程式碼

本作品採用《CC 協議》,轉載必須註明作者和本文連結
微信公眾號:福大大架構師每日一題。最新面試題,涉及golang,rust,mysql,redis,雲原生,演算法,分散式,網路,作業系統。

相關文章