2021牛客暑期多校第一场A题(爱丽丝和鲍勃)题解

题目大意

有一种石子游戏,爱丽丝先手,爱丽丝和鲍勃轮流操作。有两堆石子,每次可以从一堆石子中取出k(k>0)个,并且从另一堆石子中取出s×k(s≥0)个。谁不能进行操作谁就输了。如果爱丽丝和鲍勃足够聪明,给出石子数量,问谁会赢。数据范围:每堆石子数量在0~5000之间,有10000次以内的询问。

样例输入

5
2 3
3 5
5 7
7 5
7 7

样例输出

Bob
Alice
Bob
Bob
Alice

思路

这是一道博弈问题,在纸上推算一定数目的样例找不到规律,因此使用SG函数利用计算机的高性能推演出所有的可能情况。设先手必胜的点为必胜点,后手必胜的点为必败点,则(0,0)为必败点。

由SG第一定理(一个局面的所有后继状态都是必胜态,则此局面为必败态)知,可以从必败点(0,0)开始,根据取石子的游戏规则,推导出其后继所有的必胜点(类似动态规划)。用暴力的方式,枚举推导完所有的状态,刚好不会超时。

注意不需要使用memset初始化SG数组,否则超时。这是由于暴力的时间复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn),n取5000时不考虑常数因子就几乎超过 1 0 8 10^8 108,再使用memset就又增加 2.5 × 1 0 7 2.5×10^7 2.5×107,故不用,编译器会自动初始化SG函数为0,即必败态。

考点

SG函数
枚举和暴力

AC代码

#include<iostream>
using namespace std;
const int maxn = 5e3 + 2;
bool sg[maxn][maxn];
void get_sg() {
	for (int i = 0; i <= 5000; i++) {
		for (int j = 0; j <= 5000; j++) {
			if (sg[i][j] == 0) {
				for (int k = 1; i + k <= 5000; k++) {
					for (int s = 0; j + s*k <= 5000; s++) {
						sg[i + k][j + s*k] = 1;
					}
				}
				for (int k = 1; j + k <= 5000; k++) {
					for (int s = 0; i + s*k <= 5000; s++) {
						sg[i + s*k][j + k] = 1;
					}
				}
			}
		}
	}
}
int t, n, m;
int main()
{
	get_sg();
	cin >> t;
	while (t--) {
		cin >> n >> m;
		sg[n][m] == 0 ? puts("Bob") : puts("Alice");
	}
    return 0;
}

心得

本题考察博弈论,SG函数是博弈问题中常用的一种经典解题方法,需要熟练掌握。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

keguaiguai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值