Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。如果你胜,你第1次怎样取子?
Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,且a<=b。a=b=0退出。
Output
输出也有若干行,如果最后你是败者,则为0,反之,输出1,并输出使你胜的你第1次取石子后剩下的两堆石子的数量x,y,x<=y。如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况.
Sample Input
1 2 5 8 4 7 2 2 0 0
Sample Output
0 1 4 7 3 5 0 1 0 0 1 2
解题思路:
这题彻底考察了对威佐夫博弈的掌握程度。详见http://blog.csdn.net/userluoxuan/article/details/38336287
AC代码:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxn = 500005;
int a[maxn], b[maxn], pos = 0;
void Func()
{
for(int i = 0;;i++)
{
a[i] = (int)(i * ((sqrt(5.0) + 1) / 2));
b[i] = a[i] + i;
if(b[i] > 1000000)
{
pos = i;
break;
}
}
}
int Find(int num)
{
int low = 0, high = pos, mid;
while(high >= low)
{
mid = (high + low) / 2;
if(a[mid] == num)
return mid;
if(a[mid] > num)
high = mid - 1;
if(a[mid] < num)
low = mid + 1;
}
return -1;
}
int Find2(int num)
{
int low = 0, high = pos, mid;
while(high >= low)
{
mid = (high + low) / 2;
if(b[mid] == num)
return mid;
if(b[mid] > num)
high = mid - 1;
if(b[mid] < num)
low = mid + 1;
}
return -1;
}
int main()
{
int m, n;
Func();
while(scanf("%d%d", &m, &n) && m && n)
{
int k = n - m;
if((int)(k * ((sqrt(5.0) + 1) / 2)) == m)
printf("0\n");
else
{
printf("1\n");
printf("%d %d\n", a[n - m], b[n - m]);
int j = Find(m);
if(j >= 0 && n > b[j])
printf("%d %d\n", a[j], b[j]);
if(j < 0)
{
int l = Find2(m);
printf("%d %d\n", a[l], m);
}
}
}
return 0;
}