Euclid 博弈:商 ⌊a/b⌋ ≥ 2 或整除时执子者必胜,商为 1 时取法唯一、胜负随轮次翻转,辗转相除 O(log V) 模拟到首个可胜局面即可。
OJ: roj
题目 ID: 1218
难度:普及-
标签:博弈论数学python
创建: 2026-09-30 00:03
更新: 2026-10-05 05:50
题目描述
有两堆石子,两人轮流取;每次只能从较多的一堆取,取的数量必须是较少一堆的整数倍。把任意一堆取空者获胜。多组输入,每行两个正整数 0 0 结束。
思路
设
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 00:03
# update_at: 2026-09-30 00:03
import sys
def current_wins(a: int, b: int) -> bool:
"""(a, b) 局面下当前执子者是否必胜。
商 a//b >= 2 时必胜(可以调整取 k 倍把必败局面留给对手);
a % b == 0 时直接取空一堆获胜;
否则商为 1,只有 (a, b) -> (b, a-b) 唯一取法,胜负随轮次翻转。
"""
a, b = max(a, b), min(a, b) # 保证 a >= b,较多堆是 a
mine = True # True = 起始先手执子
while a // b < 2 and a % b != 0: # 商为 1 且不整除:唯一取法
a, b = b, a - b # 较多堆变 b,较多堆变 a-b(更小)
mine = not mine # 唯一取法后换人执子
return mine # 退出时比值 >=2 或整除:执子者获胜
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
out: list[str] = []
while True:
a, b = next(data), next(data) # 每组一行两个正整数
if a == 0 and b == 0: # 0 0 结束
break
out.append("win" if current_wins(a, b) else "lose")
print("\n".join(out))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 05:50
* update_at: 2026-10-05 05:50
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
// 当前(a,b)局面(a>=b)下,执子者是否必胜
// 商>=2 或整除时当前执子者可直接获胜;否则只能(a,b)->(b,a-b),胜负翻转
bool current_wins(ll a, ll b) {
bool mine = true; // true 表示起始先手在此局面执子
while (a / b < 2 && a % b != 0) { // 商为1且不整除:唯一取法
ll nb = a - b; // 取完后较少堆变为 a-b
a = b;
b = nb;
mine = !mine; // 唯一取法后换人
}
return mine; // 比值>=2或整除:当前执子者获胜
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
ll a, b;
while (cin >> a >> b) {
if (a == 0 && b == 0) break;
if (a < b) swap(a, b); // 保证 a 是较多堆
cout << (current_wins(a, b) ? "win" : "lose") << "\n";
}
return 0;
}