UVa 10368 - Euclid's Game

题目:有两个数字(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;
}
 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值