main
黄海 2 years ago
parent ea36955eb8
commit 39bde88db1

@ -1,40 +1,53 @@
#include <bits/stdc++.h> #include <cstdio>
using namespace std; using namespace std;
const int N = 1e3 + 5;
int a[N], L[N][N], R[N][N]; const int N = 1010;
int n;
int a[N];
int left[N][N], right[N][N];
int main() { int main() {
int T; int T;
cin >> T; scanf("%d", &T);
while (T--) { while (T--) {
int n; scanf("%d", &n);
cin >> n; for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
cin >> a[i]; for (int len = 1; len <= n; len++)
L[i][i] = R[i][i] = a[i];
}
for (int len = 2; len <= n; len++)
for (int i = 1; i + len - 1 <= n; i++) { for (int i = 1; i + len - 1 <= n; i++) {
int j = i + len - 1, l = L[i][j - 1], r = R[i][j - 1], x = a[j]; int j = i + len - 1;
if (x == r) if (len == 1)
L[i][j] = 0; left[i][j] = right[i][j] = a[i];
else if (x >= l && x < r) else {
L[i][j] = x + 1; int L = left[i][j - 1], R = right[i][j - 1], X = a[j];
else if (x > r && x <= l) if (R == X)
L[i][j] = x - 1; left[i][j] = 0;
else if (X < L && X < R || X > L && X > R)
left[i][j] = X;
else if (L > R)
left[i][j] = X - 1;
else else
L[i][j] = x; left[i][j] = X + 1;
l = L[i + 1][j], r = R[i + 1][j], x = a[i]; L = left[i + 1][j], R = right[i + 1][j], X = a[i];
if (x == l) if (L == X)
R[i][j] = 0; right[i][j] = 0;
else if (x >= r && x < l) else if (X < L && X < R || X > L && X > R)
R[i][j] = x + 1; right[i][j] = X;
else if (x > l && x <= r) else if (R > L)
R[i][j] = x - 1; right[i][j] = X - 1;
else else
R[i][j] = x; right[i][j] = X + 1;
}
} }
puts(L[2][n] == a[1] ? "0" : "1");
if (n == 1)
puts("1");
else
printf("%d\n", left[2][n] != a[1]);
} }
return 0; return 0;
} }

@ -66,37 +66,62 @@ $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为 **必胜局面**
**反证法**: **反证法**:
假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。 假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。
#### 4、推论
有了上面推的$left[i][j]$唯一性,得出一个有用的推论:
**对于任意非负整数 $x \neq left(i,j)$$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面**
#### 5、定义$L$和$R$
因为下面的讨论中出现的$L$和$R$的含义不是很好理解,我们先把这个概念理清楚:
![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png)
**$Q$**:为什么要这么定义$L$和$R$呢?
答:<font color='red' size=4><b>博弈论的题目都是可以通过动态规划来递推的</b></font>
为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以
> **博弈论 $\Leftrightarrow $ 动态规划 $\Leftrightarrow $ 递归**
递推嘛,就是类似于 **数学归纳法**,先求出初始状态是多少,然后假设$i \sim j-1$这段已经计算出$left[i][j-1],right[i][j-1]$了,现在想依赖于这两个数值推导出$left[i][j],right[i][j]$,怕写的太长麻烦,就定义了$L=left[i][j-1],R=right[i][j-1]$
**$Q$:那为什么是定义先手必败,而不是先手必胜呢?**
答:因为上面证明过定义先手必败的动态规划数组,是肯定存在并且是唯一的,这样才能正常计算啊。
考虑三个问题:
- ① 初始值
- ② 答案在哪
- ③ 递推式
> **注:答案在哪,并不是和递推式相关,而是和状态表示相关,一定要注意**
#### 4、边界情况
$$\LARGE left[i][i]=a_i$$
当只有一堆石子时,我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子****后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** **① 初始值**
$\large L[i][i]=R[i][i]=a_i$
#### 5、递推关系 当只有一堆石子时($only$ $i$),我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子****后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败**
**② 答案在哪**
**先手必败** $\Leftrightarrow \ L[2][n]=a_1$
> **解释**:如果$L[2][n]$与$a[1]$相等,就意味着本来挺好的$a[2] \sim a[n]$,结果,前面放上了一个$a[1]$,而加上的$a[1]$使得现在的局面必败,先手必败。
**③ 递推式**
* 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子 * 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子
* 让我们使用$left[i][j-1]$和$right[i][j-1]$来表示$left[i][j]$和$right[i][j]$,形成$DP$递推关系 * 让我们使用$left[i][j-1]$和$right[i][j-1]$来表示$left[i][j]$和$right[i][j]$,形成$DP$递推关系
> 前面动作都按要求整完了,问我们:本步骤,我们有哪些变化,根据这些变化,怎么样用前面动作积累下来的数据来完成本步骤数据变化的填充,这不就是动态规划吗?
![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) #### 分类讨论
#### 6、推论 - **特殊情况:$L=R=0$**
有了上面推的$left[i][j]$唯一性,得出一个有用的推论:
**对于任意非负整数 $x \neq left(i,j)$$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面**
#### 7、特殊情况:$L=R=0$ 令 $\displaystyle \large x=a[j](x>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$。 若 $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> > <font color='red'><b>注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$,而且已经证明了$L=R=0$是唯一的。</b></font>
#### 8、分类讨论
* $x=R$$Case$ $1$ * $x=R$$Case$ $1$
最简单的情况——根据 $R=right[i][j-1]$ 的定义,区间 $[i,j]$ 本来就是必败局面,因此左边啥也不能添,添了反而错,故 最简单的情况——根据 $R=right[i][j-1]$ 的定义,$x=R$则区间 $[i,j]$ 是必败局面,因此左边啥也不能添,添了反而错,故
$$\large left[i][j]=0$$ $$\large left[i][j]=0$$
* $x<R$ * $x<R$
@ -119,7 +144,7 @@ $$\LARGE left[i][i]=a_i$$
* 若先手拿最左边一堆,设拿了以后 **还剩 $z$ 个石子** * 若先手拿最左边一堆,设拿了以后 **还剩 $z$ 个石子**
* 若 $z>L$,则后手将最右堆拿成 $z-1$ 个石子($z-1 \ge L>0$**保证左侧比右侧多$1$个石子**,就能回到 $Case$ $3$ 本身,递归证明即可 * 若 $z>L$,则后手将最右堆拿成 $z-1$ 个石子($z-1 \ge L>0$**保证左侧比右侧多$1$个石子**,就能回到 $Case$ $3$ 本身,递归证明即可
* 若 $z=L$,则后手将最右堆拿完,根据 $L[i][j-1]$ 定义知此时局面必败 * 若 $z=L$,则后手将最右堆拿完,根据 $L=left[i][j-1]$ 定义知此时局面必败
* 若 $0<z<L$,则后手将最右堆拿成 $z$ 个石子,**由 $Case$ $2$ 知此时是必败局面** * 若 $0<z<L$,则后手将最右堆拿成 $z$ 个石子,**由 $Case$ $2$ 知此时是必败局面**
* 若 $z=0$,此时最右堆石子数 $x$ 满足 $L \le x<R$,结合 $right[i][j-1]$ 定义知 **此局面必胜** * 若 $z=0$,此时最右堆石子数 $x$ 满足 $L \le x<R$,结合 $right[i][j-1]$ 定义知 **此局面必胜**
<br> <br>
@ -178,7 +203,6 @@ $$
同理可求 $R(i,j)$。 同理可求 $R(i,j)$。
回到原题,**先手必败当且仅当** $L[2][n]=a_1$ ,于是我们就做完啦!
时间复杂度 $O(n^2)$。 时间复杂度 $O(n^2)$。

@ -33,369 +33,143 @@ $1≤T≤10,1≤n≤1000,1≤a_i≤10^9$
0 0
``` ```
### 解题思路 ### 二、思考过程
首先定义两个状态:
① $left[i][j]$ : 我在$[i,j]$这个区间的左边放上一堆数量是多少的石头能够让我先手必败. #### 1、状态定义
② $right[i][j]$: 我在$[i,j]$区间的左边放上一堆数量是多少的石头能让我先手必败.
① 设 $left[i][j]$ 表示在 $[i,j]$ 已经固定的区间 **左侧** 放上一堆数量为 $left[i][j]$ 的石子后,**先手必败**
$left[i][j]$ 的性质:**必然存在且唯一**. ② 设 $right[i][j]$ 表示在 $[i,j]$ 已经固定的区间 **右侧** 放上一堆数量为 $right[i][j]$ 的石子后,**先手必败**
**证明**:假设在区间左边放上$L_1,L_2$ 数量的石堆都能使我必败,不妨设$L_1<L_2$,,$L_2$$L_1$,,.,. 即:$(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]$ 是通过递推的方式获得,$right[i][j]$与之对称,因此同理递推也可获得. 有如下两个性质:
最后的答案是:$left[2][n]!=a[1]$ #### 2、$left[i][j]$,$right[i][j]$一定存在
**反证法**:
递推的过程: 假设不存在满足定义的 $left[i][j]$,则对于 **任意非负整数** $x$,有形如:
对于给定的[i,j]区间,我们定义X=a[j] $$\large \underbrace{x,a_i,a_{i+1},\cdots,a_j}_{A(x)}$$ 都为 **必胜局面** ,记为 $A(x)$ 局面。
<EFBFBD>
= 由于 $A(x)$ 为必胜局面,故从 $A(x)$ 局面 <font color='red' size=4><b>必然存在若干种办法一步可达必败局面</b></font>
<EFBFBD>
[ 若从最左边一堆中拿,<font color='red' size=4><b>因为假设原因,不可能变成必败局面</b></font>,因为这样得到的局面仍形如 $A(x)$。
<EFBFBD>
]
, L=left[i][j1] 左边拿没用,只能考虑从右边拿(即从$a_j$里拿)
<EFBFBD>
= 于是设 $A(x)$ 一步可达的某个 **必败局面**为 $(x,a_i,a_{i+1},\cdots,a_{j-1},y)$,显然有 $0 \le y < a_j$。
<EFBFBD>
<EFBFBD> **由于 $x$ 有无限个,但 $y$ 只有 $a_j$种——根据抽屉原理,必然存在 $x_1,x_2(x_1 > 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)$ 都是必败局面**。但这两个必败局面之间 **实际一步可达**(比如拿走$x_1-x_2$个),矛盾,假设不成立,原命题成立。
<EFBFBD>
<EFBFBD> #### 3、$left[i][j]$,$right[i][j]$必然唯一
[
<EFBFBD> **反证法**:
] 假设 $left(i,j)$ 不唯一,则存在非负整数 $x_1,x_2(x_1 \neq x_2)$,使得$(x_1,a_i,a_{i+1},⋯,a_{j1},a_j)$ 和 $(x_2,a_i,a_{i+1},\cdots,a_{j-1},a_j)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$x_1-x_2$个石子到达另一个 **必败局面** ,矛盾,假设不成立,原命题成立。
[
<EFBFBD> #### 4、推论
有了上面推的$left[i][j]$唯一性,得出一个有用的推论:
1 **对于任意非负整数 $x \neq left(i,j)$$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面**
]
,R=right[i][j1] #### 5、定义$L$和$R$
<EFBFBD> 因为下面的讨论中出现的$L$和$R$的含义不是很好理解,我们先把这个概念理清楚:
=
<EFBFBD> ![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png)
<EFBFBD>
<EFBFBD>
**$Q$**:为什么要这么定义$L$和$R$呢?
<EFBFBD> 答:<font color='red' size=4><b>博弈论的题目都是可以通过动态规划来递推的</b></font>
[ 为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以
<EFBFBD> > **博弈论 $\Leftrightarrow $ 动态规划 $\Leftrightarrow $ 递归**
]
[ 递推嘛,就是类似于 **数学归纳法**,先求出初始状态是多少,然后假设$i \sim j-1$这段已经计算出$left[i][j-1],right[i][j-1]$了,现在想依赖于这两个数值推导出$left[i][j],right[i][j]$,怕写的太长麻烦,就定义了$L=left[i][j-1],R=right[i][j-1]$
<EFBFBD>
$left[i][j]$递推
1
] `?[i j-1] 第j堆(石子个数x)`
,即在[i,j-1]区间的左边放L先手必败,在右边放R先手必胜. 则我们希望求的是假设$i\sim j$已经固定了,我们在左边放多少个可以使得`?[i j-1] 第j堆` 是必败的
定义:
情况1: 左边放$L$时,`L[i j-1]`必败
右边放$R$时,`[i j-1]R`必败
当R=x
<EFBFBD>
= **$Q$:那为什么是定义先手必败,而不是先手必胜呢?**
<EFBFBD> 答:因为上面证明过定义先手必败的动态规划数组,是肯定存在并且是唯一的,这样才能正常计算啊。
,left[i][j]=0
<EFBFBD> 考虑三个问题:
<EFBFBD> - ① 初始值
<EFBFBD> - ② 答案在哪
<EFBFBD> - ③ 递推式
[ > **注:答案在哪,并不是和递推式相关,而是和状态表示相关,一定要注意**
<EFBFBD>
]
[ **① 初始值**
<EFBFBD> $\large L[i][i]=R[i][i]=a_i$
]
= 当只有一堆石子时($only$ $i$),我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子****后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败**
0
;因为X=R
<EFBFBD> **② 答案在哪**
= **先手必败** $\Leftrightarrow \ L[2][n]=a_1$
<EFBFBD> > **解释**:如果$L[2][n]$与$a[1]$相等,就意味着本来挺好的$a[2] \sim a[n]$,结果,前面放上了一个$a[1]$,而加上的$a[1]$使得现在的局面必败,先手必败。
的时候[i,j]已经先手必败,左边就不用放了
情况2: **③ 递推式**
当X<L * 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子
<EFBFBD> ![](https://cdn.acwing.com/media/article/image/2022/04/04/145584_d2d0424bb4-%E6%97%A0%E6%A0%87%E9%A2%98.jpg)
<
<EFBFBD>
且X<R
<EFBFBD> ### 六、实现代码
< ```cpp {.line-numbers}
<EFBFBD> #include <cstdio>
,left[i][j]=X
<EFBFBD> using namespace std;
<EFBFBD> const int N = 1010;
<EFBFBD> int n;
<EFBFBD> int a[N], l[N][N], r[N][N];
[
<EFBFBD> int main() {
] int T;
[ scanf("%d", &T);
<EFBFBD> while (T--) {
] scanf("%d", &n);
= for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
<EFBFBD>
;left[i][j]=X for (int len = 1; len <= n; len++)
<EFBFBD> for (int i = 1; i + len - 1 <= n; i++) {
<EFBFBD> int j = i + len - 1;
<EFBFBD> if (len == 1)
<EFBFBD> l[i][j] = r[i][j] = a[i];
[ else {
<EFBFBD> int L = l[i][j - 1], R = r[i][j - 1], x = a[j];
] if (R == x)
[ l[i][j] = 0;
<EFBFBD> else if (x < L && x < R || x > L && x > R)
] l[i][j] = x;
= else if (L > R)
<EFBFBD> l[i][j] = x - 1;
. else
证明:当先手每次取一边的石堆的时候,后手取另一边的石堆且取相同的数量.直到先手将一边的石堆取完,另一边的石堆的个数为y l[i][j] = x + 1;
<EFBFBD>
, 0=<y<x // 与上述情况对称的四种情况
0 L = l[i + 1][j], R = r[i + 1][j], x = a[i];
=< if (L == x)
<EFBFBD> r[i][j] = 0;
< else if (x < L && x < R || x > L && x > R)
<EFBFBD> r[i][j] = x;
,那么在这个过程中,先手操作完之后给后手的局面一定不是一个必败态,因为必败态存在且唯一,所以对于先手来说,这种情况下,先手必败. else if (R > L)
r[i][j] = x - 1;
情况3: else
r[i][j] = x + 1;
3.1: }
当L>R }
<EFBFBD>
> if (n == 1)
<EFBFBD> puts("1");
且R<X<=L else
<EFBFBD> printf("%d\n", l[2][n] != a[1]);
< }
<EFBFBD>
<= return 0;
<EFBFBD> }
, left[i][j]=X1 ```
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>
[
<EFBFBD>
]
[
<EFBFBD>
]
=
<EFBFBD>
1
.
先手取右边:
取完之后X>R
<EFBFBD>
>
<EFBFBD>
,那么后手可以在另一边取先手取得数量少一的数量,这样对于每次先手取完之后剩余X>R
<EFBFBD>
>
<EFBFBD>
的情况来说,后手都可以在另一边取.
取完之后X=R
<EFBFBD>
=
<EFBFBD>
,那么就对应情况1,后手把另一边的石堆取光
取完之后X<R
<EFBFBD>
<
<EFBFBD>
,那么就对应情况2,后面把另一边的石堆取到X1
<EFBFBD>
1
先手取左边:
取完之后X>=R
<EFBFBD>
>=
<EFBFBD>
,那么后手在另一边取数量加一的数量,对于每次先手取的情况,后手都可以在另一边取.
取完之后X<R
<EFBFBD>
<
<EFBFBD>
,那么后手把另一边的石堆取到X
<EFBFBD>
,这样就对应了情况2.
以上先手必败
3.2:
当R>L
<EFBFBD>
>
<EFBFBD>
且L<=X<R
<EFBFBD>
<=
<EFBFBD>
<
<EFBFBD>
left[i][j]=X+1
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>
[
<EFBFBD>
]
[
<EFBFBD>
]
=
<EFBFBD>
+
1
.
先手取右边:
取完之后X>=L
<EFBFBD>
>=
<EFBFBD>
,后手就在另一边取数量加一的石头
取完之后X<L
<EFBFBD>
<
<EFBFBD>
,就跟情况2一样,后手把左边的石堆取成跟右边的数量相同
先手取左边:
取完之后X>=L+1
<EFBFBD>
>=
<EFBFBD>
+
1
,后手就在另一边取数量减一的石头
取完之后X<=L
<EFBFBD>
<=
<EFBFBD>
,后手就把右边的石头取完
取完之后X<L
<EFBFBD>
<
<EFBFBD>
,后手就把右边取得跟左边数量一样.
以上先手必败
情况4
X>L
<EFBFBD>
>
<EFBFBD>
且X>R
<EFBFBD>
>
<EFBFBD>
, left[i][j]=X
<EFBFBD>
<EFBFBD>
<EFBFBD>
<EFBFBD>
[
<EFBFBD>
]
[
<EFBFBD>
]
=
<EFBFBD>
4.1:
X>L>R
<EFBFBD>
>
<EFBFBD>
>
<EFBFBD>
:
先手取左边:
取完之后X>L>R
<EFBFBD>
>
<EFBFBD>
>
<EFBFBD>
,那么后手在右边取相同数量的石子
取完之后X==L
<EFBFBD>
==
<EFBFBD>
,那么后手把右边的石堆取完
取完之后R<X<L
<EFBFBD>
<
<EFBFBD>
<
<EFBFBD>
,那么后手把右边的石堆取到比左边的石堆多一
先手取右边:
取完之后X>L>R
<EFBFBD>
>
<EFBFBD>
>
<EFBFBD>
,那么后手在左边取相同数量的石子
取完之后R<X<=L
<EFBFBD>
<
<EFBFBD>
<=
<EFBFBD>
,跟情况3.1一样,那么后手把左边的石堆数量取成X1
<EFBFBD>
1
4.2:
X>R>L
<EFBFBD>
>
<EFBFBD>
>
<EFBFBD>
:
先手取左边:
取完之后X>R>L
<EFBFBD>
>
<EFBFBD>
>
<EFBFBD>
,那么后手在右边取相同数量的石子
取完之后L<X<=R
<EFBFBD>
<
<EFBFBD>
<=
<EFBFBD>
,那么后手就把右边石堆的数量取到X1
<EFBFBD>
1
先手取右边:
取完之后X>R>L
<EFBFBD>
>
<EFBFBD>
>
<EFBFBD>
,那么后手就在左边取相同的数量的石子
取完之后L<=X<R
<EFBFBD>
<=
<EFBFBD>
<
<EFBFBD>
,就跟情况3.2一样,那么后手就左边的石堆数量取成X+1
<EFBFBD>
+
1
以上先手必败
Loading…
Cancel
Save