zoukankan      html  css  js  c++  java
  • Uva1378

    题目位置: 2006年北京赛区的题目

                   http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4124    

    题意:给定n个石堆,每个石堆的石子个数不超过1000,A与B进行一向游戏,每次一个人选取3堆(i,j,k, i <j <=k),然后取走i堆的一个石子,给j,k各加入一个石子,(j,k)相同的话就两个,最后不能取的会输。问先取者是否有必胜策略,有的话输出第一步选取的3堆,否则输出-1 -1 -1。

    思路:sg函数的变形,关键在于如何拆分游戏。

            我们知道每次选取i,j,k,i取走一个,j,k各增加一个,这样的话就改变了i,j,k的奇偶性,所以对于所有的石子堆,只有当他为奇数时才有效果,偶数的话不影响结局。(因为偶数的话,第一个可以取i,j,k,那么第二个也可以取i,j,k,奇偶性不变,结果与第一个取之前的局面是一样的)

           这样一来我们只要考虑奇数堆。。不妨设其棋子数为1个

           接下来我们就讨论一下如何拆分。。

           对于i上单个棋子,那么他可以给他后面任意选取的两堆各增加一个棋子,而增加的棋子也有同样的方法去给其后面的增加棋子。

           这样一来,只要把i生出来的棋子全部归到第n堆,这便是一个对于i的单个棋子的游戏。。

           所以很明显的,每个棋子都是一个单个的游戏,这样就拆分了游戏。。

          接下来就是如何用sg函数表示了,

             对于第i堆的每个石子,我们可以n-i-1个石子来表示他(因为他后面有这么多个,跟nim联系起来),用sg[n-i-1]来表示她的sg值

            值得注意的是1):假设i可以拆分成j,k,那么其子状态sg[i] ^ sg[k]来计算,因为拆分出来的是2个独立博弈的和,

            ps.我弱弱的博弈论。。。。

    code:

      

    /*
      Problem:UVA3688
      Author:yzcstc
      Time:2013.5.24
      
    */
    
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cstdlib>
    
    using namespace std;
    int sg[100], stone[30], tot, n;
    
    int find_sg(int v){
        if (sg[v] != -1) return sg[v];
        int vis[100]= {0};
        for (int i = 0; i < v; ++i)
           for (int j = i; j < v; ++j)
              vis[find_sg(i)^find_sg(j)] = 1;
        int res = 0;
        while (vis[res]) res++;
        return sg[v] = res;
    }
    
    void init(){
        memset(sg, -1, sizeof(sg));
        sg[0] = 0;
        for (int i = 1; i < 23; ++i){
            
            find_sg(i);
           // printf("%d\n", sg[i]);
        }
    }
    
    void solve(){
        ++tot;
        int now = 0;
        for (int i = 0; i < n; ++i){
            scanf("%d", &stone[i]);
            if (stone[i]&1)  now ^= sg[n - i - 1];
        }
        for (int i = 0; i < n; ++i)
           if (stone[i])
             for (int j = i + 1; j < n; ++j)
               for (int k = j; k < n; ++k)
               if ((now ^ sg[n - k - 1] ^ sg[n - j - 1] ^ sg[n - i - 1]) == 0){
                    printf("Game %d: %d %d %d\n", tot, i, j, k);
                    return ;
               }
        printf("Game %d: -1 -1 -1\n", tot);
    }
    
    int  main(){
        freopen("uva3688.in", "r", stdin);
        freopen("uva3688.out","w",stdout);
        init();
        while (scanf("%d", &n)!= EOF && n)
             solve();
        //fclose(stdin); fclose(stdout);
    }
    View Code

      

  • 相关阅读:
    使用Python从Workflowy同步大纲到印象笔记
    使用Jenkins自动部署博客
    使用有限状态机原理实现英文分词
    TeamFlowy——结合Teambition与Workflowy提高生产力
    技巧收集-M1709
    Tenacity——Exception Retry 从此无比简单
    记住 Python 变量类型的三种方式
    Python 装饰器装饰类中的方法
    技巧收集-W1701
    DotnetSpider爬虫简单示例 net core
  • 原文地址:https://www.cnblogs.com/yzcstc/p/3097557.html
Copyright © 2011-2022 走看看