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;
}
}
執行結果如下:
本作品採用《CC 協議》,轉載必須註明作者和本文連結