BZOJ 1188 [SG定理][博弈论]

本文介绍了一个名为“分裂”的游戏,玩家需要通过特定的规则来移动巧克力豆以获得胜利。文章详细解析了游戏规则,并利用SG定理进行策略分析,探讨了如何确保获胜的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

聪聪和睿睿最近迷上了一款叫做分裂的游戏。 该游戏的规则试: 共有 n 个瓶子, 标号为 0,1,2..n1, 第 i 个瓶子中装有 pi颗巧克力豆,两个人轮流取豆子,每一轮每人选择 3 个瓶子。标号为 i,j,k, 并要保证 i<j,jk 且第 i 个瓶子中至少要有 1 颗巧克力豆,随后这个人从第 i 个瓶子中拿走一颗豆 子并在j,k中各放入一粒豆子(j 可能等于 k) 。如果轮到某人而他无法按规则取豆子,那么他将输 掉比赛。胜利者可以拿走所有的巧克力豆! 两人最后决定由聪聪先取豆子,为了能够得到最终的巧克力豆,聪聪自然希望赢得比赛。他思考了一下,发现在有的情况下,先拿的人一定有办法取胜,但是他不知道对于其他情况是否有必胜 策略,更不知道第一步该如何取。他决定偷偷请教聪明的你,希望你能告诉他,在给定每个瓶子 中的最初豆子数后是否能让自己得到所有巧克力豆,他还希望你告诉他第一步该如何取,并且为 了必胜,第一步有多少种取法? 假定 1<n21,pi10000

Solution

SG定理的基本应用吧。SGi表示第i个瓶子中的豆子,接下来就是套SG定理了。

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 30000;
const int N = 30;

bool vis[MAXN];
int a[N], SG[N];
int test, G, cnt, n;

int main(void) {
    freopen("1.in", "r", stdin);
    scanf("%d", &test);
    for (int i = 1; i < 28; i++) {
        memset(vis, 0, sizeof vis);
        for (int j = 0; j < i; j++)
            for (int k = 0; k <= j; k++)
                vis[SG[j] ^ SG[k]] = true;
        for (int j = 0; ; j++)
            if (!vis[j]) {
                SG[i] = j; break;
            }
    } // 预处理SG函数
    while (test--) {
        scanf("%d", &n); G = cnt = 0;
        for (int i = 0; i < n; i++) scanf("%d", &a[i]);
        for (int i = 0; i < n; i++)
            if (a[i] & 1) G ^= SG[n - i - 1];
        for (int i = 0; i < n; i++)
            for (int j = i + 1; j < n; j++)
                for (int k = j; k < n; k++)
                    if ((G ^ SG[n - i - 1] ^ SG[n - j - 1] ^ SG[n - k - 1]) == 0)
                        if ((++cnt) == 1) printf("%d %d %d\n", i, j, k);
        if (!cnt) puts("-1 -1 -1");
        printf("%d\n", cnt);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值