bzoj1874 [BeiJing2009 WinterCamp]取石子游戏
1874: [BeiJing2009 WinterCamp]取石子游戏
Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 925 Solved: 381
[Submit][Status][Discuss]
Description
小H和小Z正在玩一个取石子游戏。 取石子游戏的规则是这样的,每个人每次可以从一堆石子中取出若干个石子,
每次取石子的个数有限制,谁不能取石子时就会输掉游戏。 小H先进行操作,他想问你他是否有必胜策略,如果有
,第一步如何取石子。
Input
输入文件的第一行为石子的堆数N
接下来N行,每行一个数Ai,表示每堆石子的个数 接下来一行为每次取石子个数的种类数M
接下来M行,每行一个数Bi,表示每次可以取的石子个数,
输入保证这M个数按照递增顺序排列。
N≤10 Ai≤1000
对于全部数据,M≤10,Bi≤10
Output
输出文件第一行为“YES”或者“NO”,表示小H是否有必胜策略。
若结果为“YES”,则第二行包含两个数,第一个数表示从哪堆石子取,第二个数表示取多少个石子,
若有多种答案,取第一个数最小的答案,
若仍有多种答案,取第二个数最小的答案。
Sample Input
4
7
6
9
3
2
1
2
7
6
9
3
2
1
2
Sample Output
YES
1 1
Hint
样例中共有四堆石子,石子个数分别为7、6、9、3,每人每次可以从任何一堆石子中取出1个或者2个石子,小H有
必胜策略,事实上只要从第一堆石子中取一个石子即可。
1 1
Hint
样例中共有四堆石子,石子个数分别为7、6、9、3,每人每次可以从任何一堆石子中取出1个或者2个石子,小H有
必胜策略,事实上只要从第一堆石子中取一个石子即可。
Source
分析:比较简单的博弈论题. 预处理出sg函数值. 将每组式子的sg异或一下看是否等于0. 输出方案的话枚举是哪一堆石子取出多少个石子,如果取出后后手必输,就是答案.利用sg函数判断.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1010; int n,a[maxn],sg[maxn],ans,Tim,vis[maxn],b[maxn],m; void init() { for (int i = 1; i <= 1000; i++) { Tim++; for (int j = 1; j <= m; j++) { if (i - b[j] >= 0) vis[sg[i - b[j]]] = Tim; } for (int j = 0; j <= 10; j++) if (vis[j] != Tim) { 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]); init(); for (int i = 1; i <= n; i++) { if (i == 1) ans = sg[a[i]]; else ans ^= sg[a[i]]; } if (ans == 0) puts("NO"); else { puts("YES"); for (int i = 1; i <= n; i++) { bool flag = false; for (int j = 1; j <= m; j++) { if (a[i] >= b[j] && (ans ^ sg[a[i]] ^ sg[a[i] - b[j]]) == 0) { printf("%d %d\n",i,b[j]); flag = 1; break; } } if (flag) break; } } return 0; }