博弈论sg函数/补漏(CF1194D 1-2-K Game)

sg函数是一个比较简单的知识点,但是一直没有学,现在学一下。
sg函数类似dp,即从阶段状态推导总体。只不过,sg函数的阶段状态只有两种

1.先手必败, 此时,sg函数应该是非零正数(笼统概括,下面细说)‘
2.先手必胜,此时,sg函数应该是0

就以这个题为例,我们设sg[k][n]是:可以走到i-1,i-2,i-k,距终点是n的sg函数。
显然:

1.sg[k][0] = 0, (因为这时全部走完, 先手已经输了)
2.sg[k][1] = 1;(因为这时先手只能走i-1,到达sg[k][0]的情况, 所以必败)
if (i < k)sg[k][i] = mex(sg[k][i-1], sg[k][i-2]);
if (i >= k)sg[k][i] = mex(sg[k][i-1], sg[k][i-2], sg[k][i-k]);
ps.定义mex运算是指不在集合中的最小非负正整数
例如mex(0,1,2,3) = 4, mex(1,2,3) = 0,mex(4,5) = 0, mex(0,0,1,2) = 3;

为什么要这么转移呢?稍加思考,我们得知,如果一个局面的接下来的几个局面全部是先手必胜(sg函数非零)那么,可知这个局面先手必败(sg函数为零),否则,这个局面的先手必应(sg函数非零),与上面的情况吻合。
另外,如果这个游戏是由几个子游戏组成,那么:

sg[G] = sg[g1] xor sg[g2] xor sg[g3] … sg[gk]

nim博弈属于这种情况,不过这个题不用,所以没有xor的阶段,我们打表:

0 1 2 3 0 4 5 6 0 7 8 9 0 10 11 12 0 13 14 15 0 16 17 18 0 19 20 21 0 22 23
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 5 0 6 7 0 8 9 10 0 11 12 0 13 14 15 0 16 17 0 18 19 20 0 21 22
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 7 0 8 9 0 10 11 0 12 13 14 0 15 16 0 17 18 0 19 20 21 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 9 0 10 11 0 12 13 0 14 15 0 16 17 18 0 19 20 0 21
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 11 0 12 13 0 14 15 0 16 17 0 18 19 0 20 21
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 13 0 14 15 0 16 17 0 18 19 0 20 21
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0
0 1 2 0 3 4 0 5 6 0 7 8 0 9 10 0 11 12 0 13 14 0 15 16 0 17 18 0 19 20 0

然而,这个题的数据量不允许我们打表,所以,我们可以通过这个表找规律
下面是ac代码:

#include <iostream>
#include <cstdio>
int sg[500][500];
using namespace std;
int main()
{
    int t;
    cin >> t;
    for (int i = 3; i <= 25; i++)
    {
        sg[i][0] = 0, sg[i][1] = 1;
        for (int j = 2; j <= 40; j++)
        {
            if (j < i)
            {
                if (sg[i][j-1] && sg[i][j-2])
                    sg[i][j] = 0;
                else sg[i][j] = max(sg[i][j-1], sg[i][j-2]);
            }
            else
            {
                if (sg[i][j-1] && sg[i][j-2] &&sg[i][j-i])
                    sg[i][j] = 0;
                else sg[i][j] = max(sg[i][j-1], max(sg[i][j-2], sg[i][j-i]));
            }
        }
    }
    while(t--)
    {
        int n, k;
        scanf("%d%d", &n, &k);
        if (n == 0)
        {
            printf("Bob\n");
            continue;
        }
        int flag = 0;
        if (k % 3 != 0)
        {
            if(n % 3 == 0) printf("Bob\n"), flag = 0;
            else printf("Alice\n"), flag = 1;
        }
        else
        {
            int ans = (n + 1) % (k + 1);
            if (ans == 0)
            {
                printf("Alice\n"), flag = 1;
            }
            else if (ans % 3 == 1)
            {
                printf("Bob\n"), flag = 0;
            }
            else printf("Alice\n"), flag = 1;

        }
        //对照表格:
       /* if (flag == sg[k][n]) cout << "AC" << endl;
        else printf("WA\n");
            */
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值