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

 

 

  

posted on 2013-05-24 20:36  yzcstc  阅读(160)  评论(0编辑  收藏  举报