Acwing_894(拆分-Nim游戏)

原题链接:

AcWing 894. 拆分-Nim游戏 - AcWing

题解:

首先需要明确一个SG函数理论:多个独立局面的SG值,等于这些局面SG值的异或和。

这个定理是SG的关键,最初在Nim游戏中,每个堆都可以被视为一个独立局面,为了确定是先手必赢态还是先手必输态,我们就需要求得所有独立局面的SG值,而这个值就等于这些局面SG值的异或和。也即:

那么在本情景下,每个堆(独立局面A)都可以进行拆分,也即A可以拆分得到很多情况的独立局面B,例如10拆分为①{1、9}②{2、8}③{3、7}④{4、6}等等。

那么这个10的sg值就需要通过这些B的sg值的异或和得到。

代码:

#include<bits/stdc++.h>
using namespace std;
int n, res;
const int N = 110;
int a[N], f[N];
unordered_set<int> S;

int sg(int x) {
	if (f[x] != -1) return f[x];
	for (int i = 0;i < x;i++)
		for (int j = 0;j <= i;j++)
			S.insert(sg(i) ^ sg(j));
	for (int i = 0;;i++) if (!S.count(i)) return f[x] = i;
}

int main(){
	cin >> n;
	memset(f, -1, sizeof(f));
	for (int i = 1;i <= n;i++) {
		int x;cin >> x;
		res ^= sg(x);
	}
	cout << (res ? "Yes" : "No");
}

疑惑点:

为啥这里的S可以开全局?

因为这题中原堆拆分成的两个较小堆小于原堆即可,因此任意一个较小堆的拆分情况会被完全包含在较大堆中,因此S可以开全局。

你可以认为是先将堆按石子数量从大到小排序,然后在第一次sg(max)的过程中就已经得到了后续所有x的f[x]值。

这是一种包含关系,大的包含小的所有情况,而之前的集合-Nim游戏堆之间没有包含关系,因此不能使用全局S。

#include<bits/stdc++.h>
using namespace std;
int n, res;
const int N = 110;
int a[N], f[N];
unordered_set<int> S;

int sg(int x) {
	if (f[x] != -1) return f[x];
	for (int i = 0;i < x;i++)
		for (int j = 0;j <= i;j++)
			S.insert(sg(i) ^ sg(j));
	for (int i = 0;;i++) if (!S.count(i)) return f[x] = i;
}

int main() {
	cin >> n;
	memset(f, -1, sizeof(f));
	for (int i = 1;i <= n;i++) cin >> a[i];
	sort(a + 1, a + 1 + n, greater<int>());//先排序得到后续所有元素的f值,减少计算次数
	for (int i = 1;i <= n;i++) res ^= sg(a[i]);
	cout << (res ? "Yes" : "No");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值