A Game Between Alice and Bob(zoj 3529)

本文深入探讨了一款基于数字操作的两人博弈游戏算法实现。通过分析游戏规则,采用SG函数和素因子计数方法,优化了算法效率,并解决了超时问题。文章详细介绍了如何通过寻找最优的数字替换策略来确保先手玩家获胜。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目戳这里

题目大意是:给你n个数字,两个人轮流每次选择一个数,将它替换成这个数的一个正因子,但不能是其本身,谁走了某一步之后所有的数都变成1了,那么这个人赢,如果先手赢还要输出第一步替换的数字的位置。

这道题折腾了好一会……刚开始一直超时,先是用常规的SG函数的求法,后来又改成计算数的素因子的个数,但是即使想到这了,因为找素因子的方法不够精到……还是超时……为什么SG值数素因子的个数呢,因为素数的SG值是等于1的,将某个数分解成它的因子,如果这些因子不是素数,那么就还可以再继续往下分,直到所有的因子都是素数,在分的过程中观察SG函数的值,也就可以发现最后这个数的SG值就是所有素因子的个数。

这是超时的找素因子的个数的求法,是从2一个一个开始网上找:

int c = 0, temp = n;
	for(i = 2; i * i <= n; i++)
	{
		while(temp % i == 0)
		{
			c ++;
			temp /= i;
		}
	}
	if(temp > 1)
		c ++;
	return sg[n] = c;



不超时的代码, 其中p数组存储的是从小到大的素数:

int c = 0, temp = n;
	for(i = 0; i < l && p[i] * p[i] <= n; i++)
	{
		while(temp % p[i] == 0)
		{
			c ++;
			temp /= p[i];
		}
	}
	if(temp > 1)
		c ++;
	return sg[n] = c;



还有就是最后输出第一步替换哪一个数字,这实际上就是nim,每个数字是一堆石子,只不过每次能取的石子数目有限制。
现在要求输出第一步选取的石子堆,其实就是在某一堆中拿走一些石子,使得所有石子的异或和是0。
条件就是:sg[i] > sum ^ sg[i],sum ^ sg[i]是除去i这一堆的石子数的异或和,如果sg[i] > sum ^ sg[i],那么第 i 堆石子就可以拿走若干个使之变成 sum ^ sg[i],这样原本的sg[1] ^ sg[2] ^…… ^sg[i] ^……sg[n]  = sum,就变成sg[1] ^ sg[2] ^…… ^sg[i] ^sum……sg[n]  = sum ^ sum = 0。这样,就将所有的数的SG值的异或和变成0,就把失败态留给对方。

#include <stdio.h>
#include <string.h>
int sg[5000050];
bool prime[5000050];
int p[5000000], l = 0;
int num[100100];

void isprime()
{
	int i, j;
	prime[1] = prime[0] = 1;
	for(i = 2; i * i < 5000010; i++)
	{
		if(!prime[i])
		{
			p[l ++] = i;
			for(j = i + i; j < 5000010; j += i)
				prime[j] = 1;
		}
	}
}

int getsg(int n)
{
	if(sg[n] != -1)
		return sg[n];
	int i;
	if(prime[n] == 0)
		return sg[n] = 1;
	/*
	bool hash[1000];
	memset(hash, 0, sizeof(hash));
	for(i = n - 1; i * i >= n; i--) 
		if(n % i == 0)
		{
			hash[getsg(i)] = 1;
			hash[getsg(n / i)] = 1;
		}
	for(i = 1; i < 1000; i++)
		if(hash[i] == 0)
			break;
	*/
	int c = 0, temp = n;
	for(i = 0; i < l && p[i] * p[i] <= n; i++)
	{
		while(temp % p[i] == 0)
		{
			c ++;
			temp /= p[i];
		}
	}
	if(temp > 1)
		c ++;
	return sg[n] = c;
}


int main (void)
{
	isprime();
	memset(sg, -1, sizeof(sg));
	int i;
	sg[0] = sg[1] = 0;
	int n, c = 0;
	while(scanf("%d", &n) != EOF)
	{
		c ++;
		int sum = 0;
		for(i = 0; i < n; i++)
		{
			scanf("%d", &num[i]);
			sum ^= getsg(num[i]);
		}
		printf("Test #%d: ", c);
		if(sum)	
		{
			printf("Alice ");
			for(i = 0; i < n; i++)
				if(sg[num[i]] > (sum ^ sg[num[i]]))
				{
					printf("%d\n", i + 1);
					break;
				}
		}
		else
			printf("Bob\n");
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值