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;
}