• 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 }
  • 相关阅读:
    redis中文API
    基于redis的分布式锁
    redis安装使用配置
    windows常用命令
    curl命令详解
    linux远程连接
    memcache在大型网站的应用策略
    缓存使用的一些注意事项
    记录memcache分布式策略及算法
    U盘安装Centos7.0图解
  • 原文地址:https://www.cnblogs.com/titicia/p/4388318.html
Copyright © 2020-2023  润新知