hdu 1729 Stone Game SG函数

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1729

题意:2个玩家,有N个箱子,每个箱子的大小是Si,游戏开始前,就有一些石子在这些箱子里了。

游戏者轮流选择箱子,然后把石子放入箱子里。并且放入的石子数量不能大于原来箱子里就有的石子的数量的平方。

比如说,一个箱子里已经有了3个石头,则可以放1-9个石头(小于箱子的容量)。

当轮到某人时,不能再放石子则为输。

问能否找到一种策略使先手必赢。

学习了一下SG函数。

在公平的组合游戏中(游戏规则对于两个玩家不加区分),可以把所有可能出现的状态看作是图的节点。如果从一个状态可以通过一步转移到另一个状态,则在两点之间连一条有向边,这样就得到了一个状态图。

假设游戏不会出现平局,即状态图是有向无环图的话,所有状态可以分为两种,P态和N态。

P态:对于前一个玩家来说是必胜的。

N态:对于后一个玩家来说是必胜的。

一个状态被称为终止状态,如果当前状态下游戏不再继续进行。大部分游戏中,终止状态都是P态。就是前一个玩家走完后,游戏结束。

从定义可以知道,任意一个P态,它要么是终止状态,要么它可以转移到的状态(后继状态)都是N态,而对于任意一个N态,它至少有一个后继状态为P态。

SG函数:对于任意x,它的SG函数值g(x) = mex{g(y)|y是x的后继状态},其中mex是一个对于非负整数集合S的运算。mex(S)为S中没有出现的最小非负整数,对于一个终止状态,因为它没有后继状态。所以它的SG函数为0。

如果知道一个状态的SG函数值,则可以快速判断该状态是P态还是N态,对于一个状态们如果这个状态的SG值等于0,那么这个状态是P态(先手必败),否则就是N态。

如果有个多个组合,则sum=sg1 ^ sg2 ^ sg3 ^ ……sgn。

结论:一个游戏的初始局面是必败态当且仅当sum=0。

参考:http://blog.163.com/scuqifuguang@126/blog/static/171370086201101711276278/

思路:

参考:http://www.cnblogs.com/vongang/archive/2011/09/27/2193375.html

因为放入的石子数量不能大于原来箱子里就有的石子的数量的平方。

设每个箱子的容量为Si,已有的石子数量为Ci.

所以寻找一个p,使得p*p+p<S

1.Ci > p。那么一次放入石子数量肯定得到Si。

因为sg(S,S) = 0。 

状态(S,S-1)的后继状态只有(S,S)。所以sg(S,S-1) = 1。同理sg(S,S-2) =2....

所以这种情况下sg(S,C) = S-C。先手必胜。

2. Ci = p。这种情况下。因为最多只能放p*p,且p*p+p<S。所以这个状态是P态。sg(S,C) = 0。先手必败。

3.Ci < P。这种情况下,到底是什么态就不确定了。

这里是递归求,再把p当成S,然后求sg。这里其实有一点不理解。

我是这样想的:

sg(p,c)。

如果求得x*x+x < p。 

如果c = x。那么接下来的玩家就不能到(S,p)这个点。

所以下下个轮到的玩家可以达到(s,p)这个点。则下下下个玩家就不能到S。而下下下个玩家可以到S。

也就是说 先手必败。

而如果C>X。那么下一个玩家就可以达到(S,p)这个点。先手必胜。

如果C<X,继续递归。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <string>
 5 #include <algorithm>
 6 #include <cmath>
 7 using namespace std;
 8 int s, c, N, ans;
 9 int sg(int s, int c){
10     int p = sqrt(double(s));
11     while(p*p+p >= s) p--;
12     if(p < c) return s-c;
13     else if(p == c) return 0;
14     else return sg(p,c); 
15 }
16 int main(){
17     int tt = 0;
18     while(scanf("%d", &N) && N){
19         int ans = 0;
20         while(N--){
21             scanf("%d%d", &s, &c);
22             ans ^= sg(s, c);
23         }
24         tt++;
25         printf("Case %d:
", tt);
26         if(ans == 0) printf("No
");
27         else printf("Yes
");
28     }
29     
30     return 0;
31 }
原文地址:https://www.cnblogs.com/titicia/p/4388318.html