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\n, 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\n"); else printf("Ollie wins\n"); } 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\n"); else printf("Ollie wins\n"); } int main() { while(scanf("%d%d", &a, &b) == 2 && a) { solve(); } return 0; }
个性签名:时间会解决一切