题目链接: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(" "); 44 } 45 46 return 0; 47 }