|
|
|
@ -1,295 +0,0 @@
|
|
|
|
|
<!-- 让表格居中显示的风格 -->
|
|
|
|
|
<style>
|
|
|
|
|
.center
|
|
|
|
|
{
|
|
|
|
|
width: auto;
|
|
|
|
|
display: table;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
margin-right: auto;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
##[$AcWing 1322$. 取石子游戏](https://www.acwing.com/problem/content/1324/)
|
|
|
|
|
|
|
|
|
|
### 一、题目描述
|
|
|
|
|
在研究过 $Nim$ 游戏及各种变种之后,$Orez$ 又发现了一种全新的取石子游戏,这个游戏是这样的:
|
|
|
|
|
|
|
|
|
|
有 $n$ 堆石子,将这 $n$ 堆石子摆成一排。
|
|
|
|
|
|
|
|
|
|
游戏由两个人进行,两人轮流操作,每次操作者都可以从 **最左** 或 **最右** 的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,**不能操作的人就输了**。
|
|
|
|
|
|
|
|
|
|
$Orez$ 问:对于任意给出的一个初始局面,是否存在先手必胜策略。
|
|
|
|
|
|
|
|
|
|
**输入格式**
|
|
|
|
|
第一行为一个整数 $T$,表示有 $T$ 组测试数据。
|
|
|
|
|
|
|
|
|
|
对于每组测试数据,第一行为一个整数 $n$,表示有 $n$ 堆石子,第二行为 $n$ 个整数 $a_i$ ,依次表示每堆石子的数目。
|
|
|
|
|
|
|
|
|
|
**输出格式**
|
|
|
|
|
对于每组测试数据仅输出一个整数 $0$ 或 $1$,占一行。
|
|
|
|
|
|
|
|
|
|
其中 $1$ 表示有先手必胜策略,$0$ 表示没有。
|
|
|
|
|
|
|
|
|
|
**数据范围**
|
|
|
|
|
$1≤T≤10,1≤n≤1000,1≤a_i≤10^9$
|
|
|
|
|
|
|
|
|
|
**输入样例**:
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
1
|
|
|
|
|
4
|
|
|
|
|
3 1 9 4
|
|
|
|
|
```
|
|
|
|
|
输出样例:
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
0
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 二、状态定义
|
|
|
|
|
|
|
|
|
|
**① 原来每堆数量是长成这个样的,可能是必胜状态,也可能是必败状态,都可以:**
|
|
|
|
|
|
|
|
|
|
<div class="center">
|
|
|
|
|
|
|
|
|
|
| $a_i$ | $a_{i+1}$ | ... | $a_{j-1}$ | $a_{j}$ |
|
|
|
|
|
| ---- | ---- |---- | ---- | ---- | ---- | ---- |
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 设 $left[i][j]$ 表示在 必胜区间 $[i,j]$ 区间的 **左侧** 放上一堆数量为 $left[i][j]$ 的石子后,**先手必败**
|
|
|
|
|
- 设 $right[i][j]$ 表示在 必胜区间 $[i,j]$ 区间的 **右侧** 放上一堆数量为 $right[i][j]$ 的石子后,**先手必败**
|
|
|
|
|
|
|
|
|
|
**② 假如原来$a_i \sim a_j$为必胜态,那么你前面添上啥都是必败的**
|
|
|
|
|
**③ 假如原来$a_i \sim a_j$为必败态,那么你前面添上$left[i][j]=0$ 也还是必败的**
|
|
|
|
|
|
|
|
|
|
**总结**:不管原来$a_i \sim a_j$是啥状态,反正,都可以通过向左边添加一个堆的方法(堆的厂子数量可以为$0$)使得状态改为 **先手必败**
|
|
|
|
|
|
|
|
|
|
<div class="center">
|
|
|
|
|
|
|
|
|
|
| $left[i][j]$ | $a_i$ | $a_{i+1}$ | ... | $a_{j-1}$ | $a_{j}$ | $right[i][j]$ |
|
|
|
|
|
| ---- | ---- |---- | ---- | ---- | ---- | ---- |
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
即:$(left[i][j],\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]})$,$(\underbrace{a_i,a_{i+1},\cdots,a_j}_{a[i]\sim a[j]},right[i][j])$ 为 **先手必败** 局面
|
|
|
|
|
|
|
|
|
|
### 三、$left[i][j]$ 的**存在性证明**
|
|
|
|
|
|
|
|
|
|
博弈论的题,时刻要记得
|
|
|
|
|
|
|
|
|
|
**转化关系**
|
|
|
|
|
|
|
|
|
|
$$
|
|
|
|
|
必胜态 \rightarrow
|
|
|
|
|
\large \left\{\begin{matrix}
|
|
|
|
|
合适的办法 & \rightarrow & 必败态(让对手必败) \\
|
|
|
|
|
走错了(傻了) & \rightarrow & 必胜态(让对手必胜)
|
|
|
|
|
\end{matrix}\right.
|
|
|
|
|
$$
|
|
|
|
|
|
|
|
|
|
$$
|
|
|
|
|
必败态 \rightarrow
|
|
|
|
|
\large \left\{\begin{matrix}
|
|
|
|
|
无论怎么走(绝望) & \rightarrow & 必胜态(让对手必胜) \\
|
|
|
|
|
永远无法(绝望) & \rightarrow & 必败态(让对手必败)
|
|
|
|
|
\end{matrix}\right.
|
|
|
|
|
$$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
($right[i][j]$ 同理,下同):
|
|
|
|
|
|
|
|
|
|
<font color='red' size=4><b>反证法:</b></font>
|
|
|
|
|
假设不存在满足定义的 $left[i][j]$,则对于 **任意非负整数** $x$,有形如:
|
|
|
|
|
|
|
|
|
|
$$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为**必胜局面**,记为 $A(x)$ 局面。
|
|
|
|
|
|
|
|
|
|
由于 $A(x)$ 为必胜局面,故从 $A(x)$ 局面 <font color='red' size=4><b>必然存在$M$种一步可达必败局面</b></font>。
|
|
|
|
|
|
|
|
|
|
若从最左边一堆中拿,<font color='red' size=4><b>因为假设原因,不可能变成必败局面</b></font>,因为这样得到的局面仍形如 $A(x)$。
|
|
|
|
|
|
|
|
|
|
<font color='red' size=4><b>注意包括此行在内的接下来几行默认 $x \neq 0$</b></font>
|
|
|
|
|
|
|
|
|
|
左边拿没用,只能考虑从右边拿:
|
|
|
|
|
于是设 $A(x)$ 一步可达的(某个)**必败局面**为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。
|
|
|
|
|
|
|
|
|
|
**由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必存在 $x_1,x_2(x_1 \neq x_2),y$ 满足 $(x_1,a_i,a_{i+1},\cdots,a_{j-1},y)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},y)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**,故矛盾,进而原命题成立。
|
|
|
|
|
|
|
|
|
|
### 四、$left[i][j]$ 的唯一性证明
|
|
|
|
|
<font color='red' size=4><b>反证法:</b></font>
|
|
|
|
|
假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j−1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而这两个必败局面之间 **实际一步可达** ,故矛盾,进而原命题成立。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 五、状态转移
|
|
|
|
|
|
|
|
|
|
#### 1、边界情况
|
|
|
|
|
$$\LARGE left[i][i]=a_i$$
|
|
|
|
|
|
|
|
|
|
当只有一堆石子时,我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败**
|
|
|
|
|
|
|
|
|
|
#### 2、递推关系
|
|
|
|
|
* 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子
|
|
|
|
|
* 让我们使用$left[i][j-1]$和$right[i][j-1]$来表示$left[i][j]$和$right[i][j]$,形成$DP$递推关系
|
|
|
|
|
|
|
|
|
|
> 前面动作都按要求整完了,问我们:本步骤,我们有哪些变化,根据这些变化,怎么样用前面动作积累下来的数据来完成本步骤数据变化的填充,这不就是动态规划吗?
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
#### 3、推论
|
|
|
|
|
有了上面推的$left[i][j]$唯一性,得出一个有用的推论:
|
|
|
|
|
**对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面**
|
|
|
|
|
|
|
|
|
|
#### 4、特殊情况:$L=R=0$
|
|
|
|
|
为方便叙述,下文记 $left[i][j-1]$ 为 $L$,记 $right[i][j-1]$ 为 $R$,并令 $\displaystyle \large x=a_j(x>0)$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。
|
|
|
|
|
|
|
|
|
|
<font color='red'><b>注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$。</b></font>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### 5、分类讨论
|
|
|
|
|
|
|
|
|
|
* $x=R$($Case$ $1$)
|
|
|
|
|
最简单的情况——根据 $R=right[i][j-1]$ 的定义,区间 $[i,j]$ 本来就是必败局面,因此左边啥也不能添,添了反而错,故
|
|
|
|
|
$$\large left[i][j]=0$$
|
|
|
|
|
|
|
|
|
|
* $x<R$
|
|
|
|
|
* $x<L$,即 $x< \min\{L,R\}$($Case$ $2$)
|
|
|
|
|
* **结论**:
|
|
|
|
|
$$\large left[i][j]=x$$
|
|
|
|
|
* **证明**:
|
|
|
|
|
即 **求证** $\large (x,a_i,a_{i+1},\cdots,a_{j-1},x)$为必败局面,其中$x< \min\{L,R\}$
|
|
|
|
|
|
|
|
|
|
由于最左边和最右边的两堆石子数量相同,后手可进行和先手 **对称** 操作,后手必将获得一个形如$(y,a_i,a_{i+1},⋯,a_{j−1})$ 或 $(a_i,a_{i+1},\cdots,a_{j-1},y)$ 的局面,其中: $0<y < x<\min\{L,R\}$
|
|
|
|
|
|
|
|
|
|
**只有左侧为$L=left(i,j-1)$这个唯一值时,才是必败态,现在不是$L$,而是$y<min(L,R)$,所以后手必胜,即先手必败**, **证毕**。
|
|
|
|
|
|
|
|
|
|
<br>
|
|
|
|
|
|
|
|
|
|
* $x \geq L$,即 $L \leq x < R$($Case$ $3$)
|
|
|
|
|
* **结论**:$$\large left[i][j]=x+1$$
|
|
|
|
|
* **证明**:
|
|
|
|
|
即 **求证** $(x+1,a_i,a_{i+1},\cdots,a_{j-1},x)$为 **必败局面** ,其中 $L \leq x <R$
|
|
|
|
|
|
|
|
|
|
* 若先手拿最左边一堆,设拿了以后 **还剩 $z$ 个石子**
|
|
|
|
|
* 若 $z>L$,则后手将最右堆拿成 $z-1$ 个石子($z-1 \ge L>0$),**保证左侧比右侧多$1$个石子**,就能回到 $Case$ $3$ 本身,递归证明即可
|
|
|
|
|
* 若 $z=L$,则后手将最右堆拿完,根据 $L[i][j-1]$ 定义知此时局面必败
|
|
|
|
|
* 若 $0<z<L$,则后手将最右堆拿成 $z$ 个石子,**由 $Case$ $2$ 知此时是必败局面**
|
|
|
|
|
* 若 $z=0$,此时最右堆石子数 $x$ 满足 $L \le x<R$,结合 $right[i][j-1]$ 定义知 **此局面必胜**
|
|
|
|
|
<br>
|
|
|
|
|
* 若先手拿最右边一堆,设拿了以后 **还剩 $z$ 个石子**
|
|
|
|
|
* 若 $z \ge L$,则后手将最左堆拿成 $z+1$个石子,就能回到 $Case$ $3$ 本身,递归证明即可
|
|
|
|
|
* 若 $0<z<L$,则后手将最左堆拿成 $z$ 个石子,**由 $Case$ $2$ 知此时是必败局面**
|
|
|
|
|
* 若 $z=0$,则后手将最左堆拿成 $L$ 个石子,由 $left[i][j-1]$定义知此时局面必败
|
|
|
|
|
|
|
|
|
|
* $x>R$
|
|
|
|
|
* $x≤L$,即 $R < x \leq L$($Case$ $4$)
|
|
|
|
|
* 结论:$$\large left[i][j]=x-1$$
|
|
|
|
|
* **证明**:
|
|
|
|
|
* 若先手拿最左边一堆,设拿了以后还剩 $z$ 个石子。
|
|
|
|
|
* 若 $z \geq R$,则后手将最右堆拿成 $z+1$ 个石子,<font color='red' size=4><b>保证左侧比右侧多$1$个石子</b></font>,就能回到 $Case$ $4$ 本身,递归证明即可。
|
|
|
|
|
* 若 $0<z<R$,则后手将最右堆拿成 $z$ 个石子,由 $Case$ $2$ 知此时是必败局面。
|
|
|
|
|
* 若 $z=0$,则后手将最右堆拿成 $R$ 个石子(注意 $Case$ $4$ 保证了此时最右堆石子个数 $>R$),由 $right[i][j-1])$ 的定义知此时是必败局面。
|
|
|
|
|
|
|
|
|
|
<br>
|
|
|
|
|
|
|
|
|
|
* 若先手拿最右边一堆,设拿了以后还剩 $z$ 个石子。
|
|
|
|
|
* 若 $z>R$,则后手将最左边一堆拿成 $z-1$ 个石子(注意 $z-1 \ge R >0$),递归证明即可。<font color='red' size=4><b>保证右侧比左侧多$1$个石子。</b></font>
|
|
|
|
|
* 若 $z=R$,则后手把最左堆拿完,根据 $right[i][j-1]$的定义可知得到了必败局面。
|
|
|
|
|
* 若 $0<z<R$,则后手将最左堆拿成 $z$ 个石子,由 $Case$ $2$ 知此时是必败局面。
|
|
|
|
|
* 若 $z=0$,此时最左堆石子数量 $k$ 满足 $0<k<L$,结合 $left[i][j-1]$ 定义知局面必胜
|
|
|
|
|
<br>
|
|
|
|
|
|
|
|
|
|
* $x>L$,即 $x>\max\{L,R\}$($Case$ $5$)
|
|
|
|
|
* **结论**:$$\large left[i][j]=x$$
|
|
|
|
|
* **证明**:
|
|
|
|
|
设先手将其中一堆拿成了 $z$ 个石子。
|
|
|
|
|
* 若 $z>\max\{L,R\}$,后手将另一堆也拿成$z$个,回到 $Case$ $5$,递归证明。
|
|
|
|
|
* 若 $0<z<\min\{L,R\}$,后手把另一堆也拿成 $z$ 个石子即可转 $Case$ $2$。
|
|
|
|
|
* 若 $z=0$,将另一堆拿成 $L$ 或 $R$ 个石子即可得到必败局面。
|
|
|
|
|
|
|
|
|
|
* 剩余的情况是 $L \le z \le R$或 $R \le z \le L$。
|
|
|
|
|
$Case$ $3$ 可以解决最左堆 $L +1 \le z \le R$,最右堆 $L \le z \le R-1$ 的情况
|
|
|
|
|
$Case$ $4$ 可以解决最左堆 $R \le z \le L-1$,最右堆 $R+1 \le z \le L$的情
|
|
|
|
|
况。
|
|
|
|
|
|
|
|
|
|
所以只需解决最左堆 $z=L$ 和最右堆 $z=R$ 的情况。而这两种情况直接把另一堆拿完就可以得到必败局面。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**综上所述:**
|
|
|
|
|
$$ \large
|
|
|
|
|
L[i][j]=
|
|
|
|
|
\large \left\{\begin{matrix}
|
|
|
|
|
0 & x=R \\
|
|
|
|
|
x+1&L \leq x < R \\
|
|
|
|
|
x-1 & R<x \leq L \\
|
|
|
|
|
x & otherwise
|
|
|
|
|
\end{matrix}\right.
|
|
|
|
|
$$
|
|
|
|
|
|
|
|
|
|
<font color='blue' size=4><b>温馨提示:</b></font>**请看清楚 $L$ 取不取等,乱取等是错的!**
|
|
|
|
|
|
|
|
|
|
同理可求 $R(i,j)$。
|
|
|
|
|
|
|
|
|
|
回到原题,**先手必败当且仅当** $L[2][n]=a_1$ ,于是我们就做完啦!
|
|
|
|
|
|
|
|
|
|
时间复杂度 $O(n^2)$。
|
|
|
|
|
|
|
|
|
|
### 六、实现代码
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
const int N = 1010;
|
|
|
|
|
int n;
|
|
|
|
|
int a[N], l[N][N], r[N][N];
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
int T;
|
|
|
|
|
scanf("%d", &T);
|
|
|
|
|
while (T--) {
|
|
|
|
|
scanf("%d", &n);
|
|
|
|
|
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
|
|
|
|
|
|
|
|
|
|
for (int len = 1; len <= n; len++)
|
|
|
|
|
for (int i = 1; i + len - 1 <= n; i++) {
|
|
|
|
|
int j = i + len - 1;
|
|
|
|
|
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];
|
|
|
|
|
if (R == x)
|
|
|
|
|
l[i][j] = 0;
|
|
|
|
|
else if (x < L && x < R || x > L && x > R)
|
|
|
|
|
l[i][j] = x;
|
|
|
|
|
else if (L > R)
|
|
|
|
|
l[i][j] = x - 1;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
```
|