Euclid`s Game

题目

给定两个整数 a 和 b,Stan和Ollie轮流从较大的数字中减去较小的数的倍数。这里的倍数是指1倍、2倍这样的整数倍,并且相减后的结果不能小于0。Stan先手,在自己的回合将其中一个数变成零的一方获胜。当双方都采取最优策略时,谁会获胜?

分析

不妨设 a<b,

情况一:若b<2a,则(a, b)只能变成(a, b-a);一旦出现 b%a==0,则先手赢。

情况二:若b>2a,则(a, b)可变成(a, b%a+a),回到了第一种情况。先手每次都能变回情况一,后手每次只能b-a,总会出现a|b,先手就能赢。也就是说这种情况先手必赢。

//还是可以套用打表模板,只是需要加上两条必胜情况的判断,不然时间复杂度太高

//在先手不能必赢的情况下,如果后手sg值不全为0,先手还能平局;如果后手sg值全为0,先手必败

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;


typedef  long long ll;
typedef pair<ll, ll> P;
const int maxn = 50;
map<P, int>mp;

int dfs(ll a, ll b)  //必胜返回1,平局返回2,必败返回-1
{
    //printf("%lld %lld
, a, b);
    if(a < b)  swap(a, b);
    int& ret = mp[make_pair(a, b)];
    if(ret)  return ret;
    if(a == 0 || b == 0)  return ret=-1;
    if(a % b == 0)  return ret=1;       //
    if(a - b > b)  return ret=1;        //加了两条必胜判断
    bool flag = false;       //能否成平局
    for(ll i = 1;b*i <= a;i++)
    {
        int tmp = dfs(a-b*i, b);
        if(tmp == -1)  return ret=1;    //后面存在必败态,先手必胜
        if(tmp == 2)  flag = true;  //存在平局

    }
    return ret=(flag ? 2 : -1);
}

int main()
{

    ll a, b;
    while(scanf("%lld%lld", &a, &b) == 2 && a)
    {
        if(dfs(a, b) == 1)  printf("Stan wins
");
        else  printf("Ollie wins
");
    }
    return 0;
}

简化版:

#include<bits/stdc++.h>
using namespace std;

int a, b;

void solve()
{
    bool flag = true;
    while(true)
    {
        if(a > b)  swap(a, b);
        if(b % a == 0)  break;
        if(b -a > a)  break;      //情况二
        b -= a;       //情况一
        flag = !flag;
    }
    if(flag)  printf("Stan wins
");
    else  printf("Ollie wins
");
}

int main()
{
    while(scanf("%d%d", &a, &b) == 2 && a)
    {
        solve();
    }

    return 0;
}
原文地址:https://www.cnblogs.com/lfri/p/11624935.html