[BZOJ 1874] [BeiJing2009 WinterCamp] 取石子游戏 【博弈论 | SG函数】

题目链接:BZOJ - 1874

 

题目分析

这个是一种组合游戏,是许多单个SG游戏的和。

就是指,总的游戏由许多单个SG游戏组合而成,每个SG游戏(也就是每一堆石子)之间互不干扰,每次从所有的单个游戏中选一个进行决策,如果所有单个游戏都无法决策,游戏失败。

有一个结论,SG(A + B + C ... ) = SG(A)^SG(B)^SG(C) ...

这道题每堆石子不超过 1000 , 所以可以把 [0, 1000] 的 SG 值暴力求出来,使用最原始的 SG 函数的定义, SG(u) = mex(SG(v))           E(u -> v) 。

然后将每一堆的 SG 值异或起来。如果必胜,就按照顺序枚举一下所有初始方案,找到必胜的就输出并退出。

 

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int MaxNum = 1000 + 5, MaxN = 10 + 5;

int n, m, Mark_Index;
int A[MaxN], B[MaxN], SG[MaxNum], Mark[MaxNum];

void Calc_SG() {
	SG[0] = 0;
	Mark_Index = 0;
	memset(Mark, 0, sizeof(Mark));
	for (int i = 1; i <= 1000; ++i) {
		++Mark_Index;
		for (int j = 1; j <= m; ++j) {
			if (B[j] > i) continue;
			Mark[SG[i - B[j]]] = Mark_Index;
		}
		for (int j = 0; j <= 1000; ++j) {
			if (Mark[j] != Mark_Index) {
				SG[i] = j; 
				break;
			}
		}
	}
}

int main() 
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%d", &A[i]);
	scanf("%d", &m);
	for (int i = 1; i <= m; ++i) scanf("%d", &B[i]);
	Calc_SG();
	int Temp = 0;
	for (int i = 1; i <= n; ++i) Temp ^= SG[A[i]];
	if (Temp == 0) printf("NO\n");
	else {
		printf("YES\n");
		bool Flag = false;
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= m; ++j) {
				if (B[j] > A[i]) continue;
				if ((Temp ^ SG[A[i]] ^ SG[A[i] - B[j]]) == 0) {
					Flag = true;
					printf("%d %d\n", i, B[j]);
					break;
				}
			}
			if (Flag) break;
		}
	}
	return 0;
}

  

[BZOJ 1874] [BeiJing2009 WinterCamp] 取石子游戏 【博弈论 | SG函数】

上一篇:把 Win 8.1 改成 Windows 2012 R2


下一篇:配置Cygwin和NDK