题目
Nova君和LaoWang决定一分胜负。给定两个正整数a,b。Nova君和LaoWang轮流从中将较大的数字减去较小数字的整数倍(1倍,2倍等等)。并且保证每次减完不会出现负数的情况。由Nova君先手。最终在自己回合将其中一个数变为0的一放获胜。两个人智商都还行,都会采取最优策略,谁会赢呢?
输入
多组测试数据。对于每组测试数据,给出两个数字a和b(保证Int范围内)
输出
对于每组数据,输出获胜者的名字。
分析
分析-1:错误的分析思路
一开始以为可以dfs,但是显然搜索空间太大,会超时。
网上有一个暴搜的居然可以过,那是因为他暴搜倍数是从大到小的,我后面的分析中可以看出,其实这个循环只会走前两个,所以能过属于巧合。
分析0:明确先手的概念
所谓先手,不仅仅是指第一次执行操作的人,也指状态和第一次操作的人相同的人。在这个问题中,和第一次执行的人状态相同是指较小的数字第一次做减数。只要较小的数字是第一次做减数,那么这个人就是先手。
举例:
假设Nova先拿到了3和10,那么3第一次做减数,Nova是先手。
如果Nova选择的k是2,那么LaoWang得到的数字是3和6,3第二次作为减数,LaoWang不是先手。
如果Nova选择k是3,那么LaoWang得到的数字是1和3,1第一次作为减数,LaoWang成为先手。
分析1:最后一轮先手胜利
如果一个人能够一直保持先手,那么他一定能赢,因为第一次出现获胜机会的时候,也就是b是a的倍数,先手先获得b-ka的机会,只要取k = b/a就赢了。退一步讲,不必保持一只是先手,只要能够保持在游戏结束前的最后一步是先手,就可以赢。所以这个游戏本质是双方抢夺最后一轮成为先手的过程。
分析2:先手地位的转化
既然双方都想抢夺最后的先手,就要考虑什么情况下先手地位会发生转化,玩家应该如何利用这些机会进行转化。如果分析0部分看得仔细,应该已经意识到这和k的取值有关。
如果k最大只能取1,那么下一轮的最小值一定是个新的数字b-a(因为b-a<a,否则k可以取2,与k最大取1矛盾),先手必然发生转化。
如果k的最大值kmax可以取大于1的数字,那么此时的玩家N拥有主动权,他可以直接把k取到最大,这样b-ka就是b%a,显然小于a,先手发生交换。
如果此时玩家N取kmax-1,那么较小的数字依然是a(因为此时b=a+b%a),那对方L没有选择只能减掉一个a,此时b变成了b%a,比a小,又轮到玩家N操作,他保持了先手地位。
也就是说,如果k的取值可以大于1,玩家可以决定谁获得先手。
如果此时玩家N取了除kmax和kmax-1以外的值,这样的话相当于把决定先手的权力让给了对方,所以不会有玩家取这样的k值。
分析3:第一个获得先手转换权的玩家取胜
按照上面的分析,可以把局面分为两类:
局面1 : k只能取1,
局面2 : k可以取大于1的数。
对于局面1,玩家只能被动的接受先手身份转换的事实。
对于局面2,玩家获得了先手转换权,既然双方都很聪明,第一次获得先手转换权的人一定可以算出后面会发生多少次被迫的先手转换,那么他可以决定自己这一次是保持还是让出先手能使自己获胜。
如果后面会出现奇数次局面1,那么当前玩家可以在本次局面2中出让先手,以后如果遇到局面2就保持先手,这样在最后一轮一定是先手,获胜。
如果后面会出现偶数次局面2,那么当前玩家可以在本次局面2中保持先手,以后如果遇到局面2就保持先手,这样在最后一轮一定是先手,获胜。
代码
#include<cstdio>
using namespace std;
int main() {
int a, b;
while (scanf("%d %d", &a, &b) != EOF) {
bool firstWin = true;
if (a > b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
int r = 1;
while (r) {
if (b % a == 0 || b > 2 * a) { //how to prove?
break;
}
firstWin = !firstWin;
r = b % a;
b = a;
a = r;
}
if (firstWin) {
printf("Nova\n");
} else {
printf("LaoWang\n");
}
}
return 0;
}