题目:有两个数字(a,b),两个人轮流从大的数字上面减去小数字的任意倍数(不能是0),
两个数字变成倍数时结束,这时轮到的人就是胜利者,给定初始状态,判断谁胜。
分析:博弈,数论。为了简化为题,让a不小于b,否则进行交换。
对于一个状态<a, b>,其中a = kb+r,那么其实只有反复游戏时,两种可能:
1.甲留给乙:<b+r,b>,下次乙只能从第一个数中减去b,留给甲<b,r>;
2.甲留给乙:<b,r>,下次乙的状态为<a1,b1>的子问题,其中a1 = b,b1 = r;
根据上面分析,构造递归解法f(a,b):
1.如果是倍数则直接结束;
2.如果a小于b的二倍(a只能减去1次b),则返回乙的相反结果!f(b,r);
3.如果乙对于f(b,r)和f(b+r,b)都可以取胜,则一定败北;
4.其他情况都会胜利(取乙不能失败的子问题即可);
复杂度:如果每次f(a,b)都会取得f(b+r,b)和f(b,r)两种状态,则复杂度最大;
T(f(a,b))= T(f(b+r,b))+ T(f(b,r))
= T(f(b,r))+ 1 + T(f(b,r))
= 2T(f(b,r))+ 1
= 2(2T(b1,r1)+ 1)+ 1
= ...
= 2^k + 2^k + ... + 1
= 2^(k+1) - 1
而计算过程是gcd(a, b),时间复杂度O(lg a)= O(k),因此T(f(a,b))= O(a);
优化:这里利用记忆化搜索优化,记录计算过程中的中间结果,采用二维链表存储;
说明:记忆化搜索变得更慢了╮(╯▽╰)╭,没有直接递归的块。
#include <stdio.h>
#include <string.h>
typedef struct _list_node
{
int second_value;
int state;
_list_node *next;
}list_node;
list_node Node[100000];
int list_node_size;
typedef struct _list_head
{
int first_value;
_list_head *next;
_list_node *second_value_list;
}list_head;
list_head List[10000], *List_Head;
int list_head_size;
void list_initial()
{
list_node_size = 0;
memset(Node, 0, sizeof(Node));
list_head_size = 0;
memset(List, 0, sizeof(List));
List_Head = NULL;
}
int list_find(int a, int b)
{
for (list_head *p = List_Head; p; p = p->next) {
if (a == p->first_value) {
for (list_node *q = p->second_value_list; q; q = q->next) {
if (b == q->second_value) {
return q->state;
}
}
}
}
return -1;
}
void list_add(int a, int b, int s)
{
list_head *find_first_list = NULL;
for (list_head *p = List_Head; p; p = p->next) {
if (a == p->first_value) {
find_first_list = p;
}
}
if (!find_first_list) {
List[list_head_size].next = List_Head;
List[list_head_size].first_value = a;
List_Head = &List[list_head_size ++];
find_first_list = List_Head;
}
list_node *find_second_list = NULL;
for (list_node *q = find_first_list->second_value_list; q; q = q->next) {
if (b == q->second_value) {
find_second_list = q;
}
}
if (!find_second_list) {
Node[list_node_size].next = find_first_list->second_value_list;
Node[list_node_size].second_value = b;
Node[list_node_size].state = s;
find_first_list->second_value_list = &Node[list_node_size ++];
}
}
int f(int a, int b)
{
int ans = list_find(a, b);
if (ans != -1) {
return ans;
}
int r = a%b;
if (r == 0) {
list_add(a, b, 1);
return 1;
}
if (a == b+r) {
ans = !f(b, r);
list_add(a, b, ans);
return ans;
}else if (f(b+r, b) && f(b, r)) {
list_add(a, b, 0);
return 0;
}else {
list_add(a, b, 1);
return 1;
}
}
int main()
{
int a, b, c;
list_initial();
while (~scanf("%d%d",&a,&b) && a+b) {
if (b > a) {
c = b;
b = a;
a = c;
}
if (f(a, b)) {
printf("Stan wins\n");
}else {
printf("Ollie wins\n");
}
}
return 0;
}