sg函数_______A Funny Stone Game(uva 1378)

The funny stone game is coming. There are n piles of stones, numbered with 0, 1, 2,..., n - 1. Two persons pick stones in turn. In every turn, each person selects three piles of stones numbered ijk (i < jj $ \leq$k and at least one stone left in pile i). Then, the person gets one stone out of pile i, and put one stone into pile j and pile k respectively. (Note: if j = k, it will be the same as putting two stones into pilej). One will fail if he can't pick stones according to the rule.

David is the player who first picks stones and he hopes to win the game. Can you write a program to help him?

The number of piles, n, does not exceed 23. The number of stones in each pile does not exceed 1000. Suppose the opponent player is very smart and he will follow the optimized strategy to pick stones.

 Input 

Input contains several cases. Each case has two lines. The first line contains a positive integer n ( $ \leq$ n$ \leq$ 23) indicating the number of piles of stones. The second line contains n non-negative integers separated by blanks, S0,...Sn-1 ( $ \leq$ Si $ \leq$ 1000), indicating the number of stones in pile 0 to pile n - 1respectively.

The last case is followed by a line containing a zero.

Output 

For each case, output a line in the format ``Game ti j k". t is the case number. ij and k indicates which three piles David shall select at the first step if he wants to win. If there are multiple groups of ijand k, output the group with the minimized lexicographic order. If there are no strategies to win the game,ij and k are equal to -1.

Sample Input 

4
1 0 1 100
3
1 0 5
2
2 1
0

Sample Output 

Game 1: 0 2 3
Game 2: 0 1 1
Game 3: -1 -1 -1


题意:

给你n堆石子,从左到右编号为0~n-1,并且告诉你每堆石子的个数(注意有的堆石子个数可能为0,但是还是为一个堆).两个人轮流操作,当某人无法执行操作时输。

每次操作为选择三个数i,j,k满足(i<j<=k)并且第i堆石子个数不能为0,然后拿走i堆一个石子,并且向j,k堆各添加一个石子。注意:j,k可以是相同的。


分析:

这个游戏满足ICG游戏,我们尝试使用SG函数来求解。

该问题如果把一个堆当作一个游戏显然不容易分析。因为再一次操作中被操作堆有的是被拿走石子,有的是添加石子。所以在这个游戏中我们把一个石子当作一个游戏,每次操作可以看作把该石子右移并分身。那么我们先将n堆序号重新编号为n-1,n-2,......1,0则最右堆为0号堆。那么一个石子被移动到0号堆就不能继续向后面移动了。所以在i号堆的某个石子的后继状态为sg[j]^sg[k]。所以 sg[i] = mex(sg[j]^sg[k]) 满足 i>j>=k。

每个堆的石子都是一样的,所以sg值都是sg[index]。index是该队的序号,所以只需要将所有石子的sg值异或起来就是整个游戏的sg值。这里需要注意一下:因为一个堆的每个石子sg值都是一样的。如果这个堆有偶数个石子。显然这个堆的所有石子的异或值为0,如果这个堆的石子个数是奇数,显然这个堆的所有石子的异或值为sg[index]。


代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 200;
int visit[maxn],sg[maxn];
void init()
{
    for(int i = 0; i <= 23 ; i ++)
    {
        memset(visit,0,sizeof(visit));
        for(int j = 0 ; j < i ; j ++)
        {
            for(int k = j ; k < i; k ++)
            {
                visit[sg[j]^sg[k]] = 1;
            }
        }
        for(int j = 0 ; j < maxn ; j ++)
            if(!visit[j])
        {
            sg[i] = j;
            break;
        }
    }

}
int a[30];

int main()
{
    init();
    int n,step,_case = 0;
    while(cin >>n)
    {
        if(n == 0)break;
        printf("Game %d: ",++_case);
        int ans = 0;
        for(int i = 0 ; i < n ; i ++)
        {
            scanf("%d",&a[i]);
            if(a[i]&1) ans ^= sg[n-i-1];
        }
        if(ans == 0)
            printf("-1 -1 -1\n");
        else
        {
            int flag = 0;
            for(int i = 0 ; i < n ; i ++)
            {
                if(a[i] == 0) continue;
                for(int j = i+1 ; j < n ; j ++)
                {
                    for(int k = j ; k < n ; k ++)
                    {
                        if((ans^sg[n-i-1]^sg[n-j-1]^sg[n-k-1]) == 0) //只需要找到字典序最小的操作使得当前局面的sg值为0,即可使得必胜。
                        {
                            flag = 1;
                            printf("%d %d %d\n",i,j,k);
                            break;
                        }
                    }
                    if(flag)break;
                }
                if(flag)break;
            }
        }
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值