HDU1536 S-Nim
题目链接:https://vjudge.net/problem/HDU-1536
题目大意:
给一个数集\(S\),稍微修改\(Nim\)游戏的规则:原本是能在一堆里面取任意个数的石子,现在变成只能取\(n(n \in S)\)个石子,当没得取石子时判负。对于给定的局面,问先手的胜负条件。
知识点: 博弈论
解题思路:
首先引用贾志豪的论文《组合游戏略述——浅谈SG游戏的若干拓展及变形》里面介绍的几个概念、定理:
游戏的和:考虑任意多个同时进行的\(SG\)组合游戏,这些\(SG\)组合游戏的和是这样一个\(SG\)组合游戏,在它进行的过程中,游戏者可以任意挑选其中的一个单一游戏进行决策,最终,没有办法进行决策的人输。
在我们每次只能进行一步操作的情况下,对于任何的游戏的和,我们若将其中的任一单一\(SG\)组合游戏换成数目为它的\(SG\)值的一堆石子,该单一\(SG\)组合游戏的规则编程取石子游戏的规则(可以任意取,甚至取完),则游戏的和的胜负情况不变。
由以上两条得到启发:我们可以把题目中的这个\(S-Nim\)游戏中的每一堆石子看成一个独立的取石子游戏(只能取数集\(S\)中的数目的石子),算出每个独立游戏的\(SG\)值,整个游戏的\(SG\)值即为各个子游戏的\(SG\)值的异或和。
而\(SG\)函数有如下性质:
(1)对于任意的局面,如果它的\(SG\)值为\(0\),那么它的任何一个后继局面的\(SG\)值不为\(0\);
(2)对于任意的局面,如果它的\(SG\)值不为\(0\),那么它一定有一个后继局面的\(SG\)值为\(0\)。
由此我们不难推出:当\(SG\)值为\(0\)时,先手必败;否则先手必胜。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 int S[105],k; 5 int sg[10005]; 6 int inp[105]; 7 bool vis[10005]; 8 void dfs(int rt){ //DFS求rt的SG值,注意要加记忆化。 9 if(sg[rt]!=-1) return; 10 for(int i=0;i<k&&rt-S[i]>=0;i++){ 11 if(sg[rt-S[i]]==-1) dfs(rt-S[i]); 12 } 13 for(int i=0;i<=k;i++) vis[i]=false; 14 for(int i=0;i<k&&rt-S[i]>=0;i++) 15 vis[sg[rt-S[i]]]=true; 16 for(int i=0;i<=rt;i++){ 17 if(!vis[i]){ 18 sg[rt]=i; 19 return; 20 } 21 } 22 } 23 int main(){ 24 int l; 25 while(scanf("%d",&k)==1&&k){ 26 for(int i=0;i<=10000;i++) sg[i]=-1; 27 for(int i=0;i<k;i++) scanf("%d",&S[i]); 28 sort(S,S+k); 29 for(int i=0;i<S[0];i++) sg[i]=0; 30 int m; 31 scanf("%d",&m); 32 while(m--){ 33 scanf("%d",&l); 34 int tmp=0; 35 for(int i=0;i<l;i++){ 36 scanf("%d",&inp[i]); 37 dfs(inp[i]); 38 tmp^=sg[inp[i]]; 39 } 40 if(tmp==0) printf("L"); 41 else printf("W"); 42 } 43 printf("\n"); 44 } 45 46 return 0; 47 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”