分裂游戏题解

题目描述

一个游戏的规则是: 共有 n 堆石子, 标号为 0,1,2…..n-1, 第 i堆石子中有p[i]个石子,两个人轮流取石子,每一轮每人选择 3 堆石子。标号为 i,j,k, 并要保证 i < j , j < = k 且第 i堆中至少要有 1 个石头,随后这个人从第 i 堆中拿走一个石子,并在 j,k中各放入一个石子(j 可能等于 k) 。

如果轮到某人而他无法按规则取石子,那么他将输掉比赛。1 < n < = 21,p[i] < = 10000 先手想要知道是否能赢下比赛,若能赢下,他希望知道第一次去石子的方案及方案数。

输入

输入文件第一行是一个整数t表示测试数据的组数,接下来为t组测试数据(t<=10)。

每组测试数据的第一行是石子堆的个数n,

接下来的一行有n个由空格隔开的非负整数,表示每堆石子的石子个数。

输出

对于每组测试数据,输出包括两行.

第一行:如无法赢得游戏,那么输出用一个空格两两隔开的三个-1,否则输出字典序最小的i,j,k.
第二行:输出方案数

样例输入

2
4
1 0 1 5000
3
0 0 1

样例输出

0 2 3
1
-1 -1 -1
0

想法

  • 经典的sg题目(sg处女题)
  • 定义每个子游戏为一个石子所在位置

算法

  • 定义sg(i)为 一个石子在位置i的sg值
  • 由于数据范围允许 sg(i)可根据定义暴力求出

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
using namespace std;
int T,n,a[25],sg[25],ans,tot;
bool flag[16385];
inline void pre()
{
    sg[1]=0;
    for (int i=2;i<=24;i++)
    {
        memset(flag,0,sizeof(flag));
        for (int j=1;j<i;j++)
            for (int k=1;k<=j;k++)
                flag[sg[k]^sg[j]]=true;
        for (int j=0;j<=16385;j++)
            if(flag[j]==false){sg[i]=j;break;}
    }
}
int main()
{
    //freopen("game.in","r",stdin);
    //freopen("game.out","w",stdout);
    scanf("%d",&T);
    pre();
    while(T--)
    {
        ans=0,tot=0;
        scanf("%d",&n); 
        for (int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for (int i=1;i<=n;i++)
            if(a[i]&1)ans^=sg[n+1-i];
        for (int i=1;i<=n;i++)
            for (int j=i+1;j<=n;j++)
                for (int k=j;k<=n;k++)
                    if((ans^sg[n+1-i]^sg[n+1-j]^sg[n+1-k])==0)
                    {
                        tot++;
                        if(tot==1)printf("%d %d %d\n",i-1,j-1,k-1);
                    }
        if(!tot)printf("-1 -1 -1\n");
        printf("%d\n",tot);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值