[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;
}

  

时间: 2024-08-02 02:48:02

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

BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏 [Nim游戏 SG函数]

小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子. N≤10 Ai≤1000 裸SG函数啊 然而我连SG函数都不会求了,WA了一会儿之后照别人代码改发现vis公用了... #include <iostream> #include <cstdio> #include <cstring> #includ

BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏(SG函数)

Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 871  Solved: 365[Submit][Status][Discuss] Description 小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子, 每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有 ,第一步如何取石子. Input 输入文件的第一行为石子的堆数N 接下来N行,每行一个数A

【BZOJ 1874】 [BeiJing2009 WinterCamp]取石子游戏

1874: [BeiJing2009 WinterCamp]取石子游戏 Time Limit: 5 Sec  Memory Limit: 162 MB Submit: 473  Solved: 186 [Submit][Status] Description 小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子. Input 输

bzoj 1874 取石子游戏 题解 &amp; SG函数初探

[原题] 1874: [BeiJing2009 WinterCamp]取石子游戏 Time Limit: 5 Sec  Memory Limit: 162 MB Submit: 334  Solved: 122 [Submit][Status] Description 小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,如果有,第一步如何取石子. In

【博弈论】【SG函数】【枚举】bzoj1874 [BeiJing2009 WinterCamp]取石子游戏

枚举第一步可能达到的状态,判断是否是必败态即可. #include<cstdio> #include<set> #include<cstring> using namespace std; int SG[1001],a[1001],b[1001],n,m,all; int sg(int x) { if(SG[x]!=-1) return SG[x]; set<int>S; for(int i=1;i<=m;++i) { if(b[i]>x) br

[HAOI2015] 数组游戏 - 博弈论,SG函数

有一个长度为N的数组,甲乙两人在上面进行这样一个游戏:首先,数组上有一些格子是白的,有一些是黑的.然后两人轮流进行操作.每次操作选择一个白色的格子,假设它的下标为x.接着,选择一个大小在1~n/x之间的整数k,然后将下标为x.2x.....kx的格子都进行颜色翻转.不能操作的人输.现在甲(先手)有一些询问.每次他会给你一个数组的初始状态,你要求出对于这种初始状态他是否有必胜策略. Solution 用暴力 SG 打个表,发现一段一段的 SG 值都是相同的,所以套个整除分块即可 #include

bzoj 1874 取石子游戏 题解 &amp;amp; SG函数初探

[原题] 1874: [BeiJing2009 WinterCamp]取石子游戏 Time Limit: 5 Sec  Memory Limit: 162 MB Submit: 334  Solved: 122 [Submit][Status] Description 小H和小Z正在玩一个取石子游戏. 取石子游戏的规则是这种,每一个人每次能够从一堆石子中取出若干个石子,每次取石子的个数有限制,谁不能取石子时就会输掉游戏. 小H先进行操作,他想问你他是否有必胜策略,假设有,第一步怎样取石子. In

BZOJ 1874 取石子游戏 (NIM游戏)

题解:简单的NIM游戏,直接计算SG函数,至于找先手策略则按字典序异或掉,去除石子后再异或判断,若可行则直接输出. #include const int N=1005; int SG[N],b[N],hash[N],a[N],sum,tmp,i,j,n,m; void FSG(int s){ SG[0]=0; for(int i=1;i<=s;i++){ for(int j=1;b[j]<=i&&j<=m;j++)hash[SG[i-b[j]]]=i; for(int j

BZOJ 1413 取石子游戏(DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1413 题意:n堆石子排成一排.每次只能在两侧的两堆中选择一堆拿.至少拿一个.谁不能操作谁输. 思路:参考这里. int f1[N][N],f2[N][N],n,a[N]; void deal() { RD(n); int i,j,k; FOR1(i,n) RD(a[i]),f1[i][i]=f2[i][i]=a[i]; int p,q,x; for(k=2;k<=n;k++) for(