zoukankan      html  css  js  c++  java
  • BZOJ 1188 [HNOI2007]*游戏

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1188

    学习SG函数的过程中,我先看了一篇叫做

    《2008-贾志豪-组合数学略述...》

    然后他开篇介绍了两篇不错的论文:

    《2002-zyf-从感性到理性...》

    《2007-王晓珂-解析一类...》

    好吧,然后我就按照推荐先看第一篇zyf的...哇感觉很好看懂啊,然后就看了一上午。

    然后SG函数的定义都没有出现,就是讲了几个题,不过人家毕竟是开端,然后我去看王晓珂的论文。

    然后我才发现原来zyf的一大篇论文在这里只是一个小小的定义+一个证明。

    好吧,我懵逼了。于是就不想看什么论文了,直接做题。

    不过那个小小的定义还是一定要提的:

      SG(x)=the min_number ∉ (x->v  SG(v))

    怎么发现这个东西的呢?还是看zyf的论文最好办...

    好了不扯了,讲讲这道题吧。

    这个游戏用另一种角度来看,可以将每一颗石子看作是一堆石子,如果它是第p堆中的石子,把么它所代表的这堆石子的个数为n-1-p。从而,操作变为拿走一个非0的石堆,并放入2个规模小于他的石堆(可以为0)。这便成了另一个游戏。之所以这么做是因为,转化后的游戏与经典的take&break游戏很相似。

    因为石子堆是互不干扰的,因此这个游戏可以看作由若干个只有一堆石子的游戏组成。

    先分析子游戏。求子游戏某状态x的SG函数值,我们需要它后继状态的SG函数值,子游戏的后继状态大多数为含有2堆石子的状态,不过2堆均小于x石子数。在了解石子数小于x的状态函数值的条件下,用SG定理可以求得任意后继状态的函数值。

    用(p)表示只剩一堆规模为p的石子的状态,(p,q)表示剩下2堆石子,规模分别为p,q的状态。g (i,j) = g (i) xor g (j) ; g(i) = min{n∈  N | n ≠ g (p,q) for i ≥ p≥ 0 且 i ≥ q ≥ 0}。[这个部分看代码+自己脑补是很好想出来的]

    应用以上结论,我们可以递推求得子游戏任意状态的SG函数值。用SG定理可以求得和游戏的任意状态的SG函数值。SG=0,David可以保证他的胜利,否则就不行。至于策略,只要操作之后留下的状态SG值为0就行了。

    时间复杂度分析:

    以上算法包含O (n)个SG值的计算,计算每一个的时间最多为O(n2),判断必胜状态需要O ( ∑Si ),寻找最优策略需要O (n3)的时间,综上,该算法的时间复杂度为O(n3+∑Si)。

     

    [唔,上面这段话我会告诉你在王晓珂的论文中就有吗?...]

     

    一个小tips:位运算的运算级别太低啦...比逻辑运算符还要低...所以异或的时候记得打括号。

     

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
     
    using namespace std;
     
    const int maxn=26;
    const int maxt=maxn*maxn*maxn;
     
    int n;
    int a[maxn];
    int SG[maxn];
    bool T[maxt];
     
    void Init(){
        for(int i=1;i<maxn;i++){
            memset(T,0,sizeof(T));
            for(int j=0;j<i;j++)
                for(int k=0;k<=j;k++)
                    T[SG[j]^SG[k]]=true;
            for(int j=0;j<maxt;j++)
                if(!T[j]){SG[i]=j;break;}
        }
    }
     
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("1188.in","r",stdin);
        freopen("1188.out","w",stdout);
    #endif
     
        int kase,ans,ai,aj,ak,cnt;
        scanf("%d",&kase);
        Init();
         
        while(kase--){
         
            ans=ai=aj=ak=cnt=0;
            scanf("%d",&n);
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                if(a[i]&1) ans^=SG[n-i];
            }
            for(int i=1;i<=n;i++)
                if(a[i])
                for(int j=i+1;j<=n;j++)
                    for(int k=j;k<=n;k++)
                        if((ans^SG[n-i]^SG[n-j]^SG[n-k])==0){
                            if(!ai) ai=i,aj=j,ak=k;
                            cnt++;
                        }
             
            printf("%d %d %d
    %d
    ",ai-1,aj-1,ak-1,cnt);
        }
     
        return 0;
    }
    View Code
  • 相关阅读:
    jackson 枚举 enum json 解析类型 返回数字 或者自定义文字 How To Serialize Enums as JSON Objects with Jackson
    Antd Pro V5 中ProTable 自定义查询参数和返回值
    ES6/Antd 代码阅读记录
    es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?
    Antd Hooks
    使用.Net Core开发WPF App系列教程(其它 、保存控件内容为图片)
    使用.Net Core开发WPF App系列教程( 三、与.Net Framework的区别)
    使用.Net Core开发WPF App系列教程( 四、WPF中的XAML)
    使用.Net Core开发WPF App系列教程( 二、在Visual Studio 2019中创建.Net Core WPF工程)
    使用.Net Core开发WPF App系列教程( 一、.Net Core和WPF介绍)
  • 原文地址:https://www.cnblogs.com/Robert-Yuan/p/5282190.html
Copyright © 2011-2022 走看看