取石子游戏(博弈dp)

发布时间 2023-08-10 20:31:57作者: nyliyuzhong

在研究过 Nim 游戏及各种变种之后,Orez 又发现了一种全新的取石子游戏,这个游戏是这样的:

有 n 堆石子,将这 n 堆石子摆成一排。

游戏由两个人进行,两人轮流操作,每次操作者都可以从最左或最右的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,不能操作的人就输了。

Orez 问:对于任意给出的一个初始局面,是否存在先手必胜策略。

输入格式

第一行为一个整数 T,表示有 T 组测试数据。

对于每组测试数据,第一行为一个整数 n,表示有 n 堆石子,第二行为 n 个整数 ai ,依次表示每堆石子的数目。

输出格式

对于每组测试数据仅输出一个整数 0 或 1,占一行。

其中 1 表示有先手必胜策略,0 表示没有。

数据范围

1T10
1n1000
1ai109

输入样例:

1
4
3 1 9 4

输出样例:

0

 

 

 

 

 

 

 代码

```

#include<iostream>
#include<cstring>

using namespace std;

const int N = 1010;

int n;
int a[N];
int l[N][N], r[N][N];

int main()
{
int T;
cin >> T;
while(T--)
{
cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
// 长度
for (int len = 1; len <= n; len ++ )
// 左右端点i 右端点j
for (int i = 1; i + len - 1 <= n; i ++ )
{
int j = i + len - 1;
// 区间只有一个堆x 则左右取为x -> x [x] x
// 只要先手取哪一堆 后手在另一堆取一样多的石子
// 则只要先手取得这堆有,后手取得另一堆也一定有,
// 直到先手取得那堆取完,后手把另一堆取完 先手必败
if (len == 1) l[i][j] = r[i][j] = a[i];
else
{
int L = l[i][j - 1], R = r[i][j - 1], X = a[j];
// 情况1
if (R == X) l[i][j] = 0;
// 情况2 情况4
else if (X < L && X < R || X > L && X > R) l[i][j] = X;
// 情况3.1 情况4.1
else if (L > R) l[i][j] = X - 1;
// 情况3.2 情况4.2
else l[i][j] = X + 1;

// 与上述情况对称的四种情况
L = l[i + 1][j], R = r[i + 1][j], X = a[i];
if (L == X) r[i][j] = 0;
else if (X < L && X < R || X > L && X > R) r[i][j] = X;
else if (R > L) r[i][j] = X - 1;
else r[i][j] = X + 1;
}
}

if (n == 1) puts("1");
else printf("%d\n", l[2][n] != a[1]);
}
return 0;
}

```