《算法竞赛·快冲300题》每日一题:“取石子游戏”

算法竞赛·快冲300题 》将于2024年出版,是《算法竞赛》的辅助练习册。

所有题目放在自建的OJ New Online Judge

用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。

文章目录

" 取石子游戏 " ,链接: http://oj.ecustacm.cn/problem.php?id=1119

题目描述

【题目描述】 有两堆石子,两个人轮流去取。每次取的时候,只能从较多的那堆石子里取,并且取的数目必须是较少的那堆石子数目的整数倍,最后谁能够把一堆石子取空谁就算赢。给定初始时石子的数目,如果两个人都采取最优策略,请问先手能否获胜。
【输入格式】 输入包含多数数据。每组数据一行,包含两个正整数a和b,表示初始时石子的数目。a, b≤109。输入以两个0表示结束。
【输出格式】 如果先手胜,输出"win",否则输出"lose"。
【输入样例】

c 复制代码
34 12
15 24
0 0

【输出样例】

c 复制代码
win
lose

题解

设a≥b。当前能取空石子的获胜,那么要求当前的局面是a=kb,即a是b的整数倍,取空a后获胜。

第一个样例"34, 12"的取法是:

甲:34-24, 12 -> 10, 12

乙:10, 12-10 -> 10, 2, 乙只有这一种取法

甲:10-10, 2 -> 0, 2, 甲胜

读者可以自行模拟第二个样例。

下面讨论不同情况下的游戏局面。设开始时a≥b。

(1)局面1:a = b。先手胜,因为他能取空一堆。

(2)局面2:b < a < 2b。此局面对先手不利,因为他没有选择,只能在a这一堆中取b,剩下a-b。新的两堆是a' = b,b' = a-b。注意有a'≠b',游戏并没有结束,先手还有机会。如果下一步仍有b' <a' < 2b',不利的局面就转到了对方。如果a' ≥ 2b',见局面(3)的讨论。

(3)局面3:a≥2b。

如果a刚好是b的整k倍,a = kb,那么先手直接取空a=kb,先手胜。

如果a不是b的整倍,先手可以取b的某个倍数,转为局面2的情况,让对方不利,并且让后续局面轮到自己时仍保持优势。

例如a=13,b=5,甲先手,这样取:

甲:13-5, 5 -> 8, 5, 下一步的(8, 5)转为局面2,让乙处于不利局面

乙:8-5, 5 -> 3, 5, 乙只有这一种选择

甲:3, 5-3 -> 3, 2, 甲也只有这一种选择

乙:3-2, 2 -> 1, 2, 乙只有这一种选择

甲:1, 2-2 -> 1, 0, 甲胜

如果甲的第一步没取对,会导致失败:

甲:13-10, 5 -> 3, 5

乙:3, 5-3 -> 3, 2, 乙只有这一种选择

甲:3-2, 2 -> 1, 2, 甲也只有这一种选择

乙:1, 2-2 -> 1, 0, 乙胜

也就是说,甲有两种选择,一种失败,一种赢,他肯定选赢的那种。

总结以上讨论,先手获胜的条件是当前处于局面1和局面3。
【重点】 局面分析 。

C++代码

在dfs()中转换游戏局面并返回结果。dfs()的复杂度是多少?第6行当b < a < 2b时执行dfs(a-b, b),每次a-b至少减一半,所以复杂度是 O ( l o g a ) O(loga) O(loga)的,当 a = 1 0 9 a=10^9 a=109时, l o g a = 30 loga = 30 loga=30。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
bool dfs(int a, int b){
    if(a < b)  swap(a, b);      //保证多的石子在前
    if(a >= 2*b || a==b) return true;  //当前处于局面1、局面3,获胜
    return !dfs(a-b, b);      //局面2,对方只能取a-b
}
int main(){
    int a, b;
    while(cin>>a>>b && (a+b)) {
        if(dfs(a, b)) cout<<"win" <<endl;
        else          cout<<"lose"<<endl;
    }
    return 0;
}

Java代码

java 复制代码
import java.util.Scanner;
public class Main {
    public static boolean dfs(int a, int b) {
        if (a < b) {
            int temp = a;
            a = b;
            b = temp;
        }
        if (a >= 2 * b || a==b)  return true;
        return !dfs(a - b, b);
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            if (a == 0 && b == 0)  break;            
            if (dfs(a, b)) System.out.println("win");
            else           System.out.println("lose");            
        }
        sc.close();
    }
}

Python代码

python 复制代码
def dfs(a, b):
    if a < b:  a, b = b, a           #交换
    if a >= 2 * b or a==b: return True
    return not dfs(a - b, b) 
while True:
    a, b = map(int, input().split())
    if a == 0 and b == 0:  break
    if dfs(a, b):  print("win")
    else:          print("lose")
相关推荐
草履虫建模19 分钟前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq2 小时前
分布式系统安全通信
开发语言·c++·算法
Jasmine_llq3 小时前
《P3157 [CQOI2011] 动态逆序对》
算法·cdq 分治·动态问题静态化+双向偏序统计·树状数组(高效统计元素大小关系·排序算法(预处理偏序和时间戳)·前缀和(合并单个贡献为总逆序对·动态问题静态化
爱吃rabbit的mq3 小时前
第09章:随机森林:集成学习的威力
算法·随机森林·集成学习
(❁´◡`❁)Jimmy(❁´◡`❁)4 小时前
Exgcd 学习笔记
笔记·学习·算法
YYuCChi4 小时前
代码随想录算法训练营第三十七天 | 52.携带研究材料(卡码网)、518.零钱兑换||、377.组合总和IV、57.爬楼梯(卡码网)
算法·动态规划
不能隔夜的咖喱5 小时前
牛客网刷题(2)
java·开发语言·算法
VT.馒头5 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
进击的小头5 小时前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机
yunteng5216 小时前
游戏全球服_基础信息
游戏·全球同服