diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp index 2a282ae..a10919f 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.cpp @@ -1,40 +1,53 @@ -#include +#include + 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 T; - cin >> T; + scanf("%d", &T); while (T--) { - int n; - cin >> n; - for (int i = 1; i <= n; i++) { - cin >> a[i]; - L[i][i] = R[i][i] = a[i]; - } - for (int len = 2; len <= n; len++) + 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, l = L[i][j - 1], r = R[i][j - 1], x = a[j]; - if (x == r) - L[i][j] = 0; - else if (x >= l && x < r) - L[i][j] = x + 1; - else if (x > r && x <= l) - L[i][j] = x - 1; - else - L[i][j] = x; - - l = L[i + 1][j], r = R[i + 1][j], x = a[i]; - if (x == l) - R[i][j] = 0; - else if (x >= r && x < l) - R[i][j] = x + 1; - else if (x > l && x <= r) - R[i][j] = x - 1; - else - R[i][j] = x; + int j = i + len - 1; + if (len == 1) + left[i][j] = right[i][j] = a[i]; + else { + int L = left[i][j - 1], R = right[i][j - 1], X = a[j]; + if (R == X) + 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 + left[i][j] = X + 1; + + L = left[i + 1][j], R = right[i + 1][j], X = a[i]; + if (L == X) + right[i][j] = 0; + else if (X < L && X < R || X > L && X > R) + right[i][j] = X; + else if (R > L) + right[i][j] = X - 1; + else + 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; -} \ No newline at end of file +} diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md index 776f5e9..6de7731 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322.md @@ -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_{j−1},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$呢? +答:博弈论的题目都是可以通过动态规划来递推的。 +为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以 +> **博弈论 $\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$递推关系 - - > 前面动作都按要求整完了,问我们:本步骤,我们有哪些变化,根据这些变化,怎么样用前面动作积累下来的数据来完成本步骤数据变化的填充,这不就是动态规划吗? - -![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/02/cccc1a0f80d1475adcf8096aa9e35ff0.png) -#### 6、推论 -有了上面推的$left[i][j]$唯一性,得出一个有用的推论: -**对于任意非负整数 $x \neq left(i,j)$,$\large (x,a_i,a_{i+1},\cdots,a_j)$为必胜局面** -#### 7、特殊情况:$L=R=0$ -为方便叙述,下文记 $left[i][j-1]$ 为 $L$,记 $right[i][j-1]$ 为 $R$,并令 $\displaystyle \large x=a_j(x>0)$ +#### 分类讨论 +- **特殊情况:$L=R=0$** -若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 +令 $\displaystyle \large x=a[j](x>0)$ -注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$。 +若 $R=0$ 则 $L=R=0$,此时 $x>\max\{L,R\}$,也就是说 $L=0$ 和 $R=0$ 都属于 $Case$ $5$,故其它 $Case$ 满足 $L,R>0$。 -#### 8、分类讨论 +> 注:因$R=0$,表示在[$i$,$j-1$]确定后,右侧为$0$就能满足[$i$,$j-1$]这一段为先手必败,此时,左侧增加那堆个数为$0$就可以继续保持原来的先手必败,即$L=0$,而且已经证明了$L=R=0$是唯一的。 * $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$$ * $xL$,则后手将最右堆拿成 $z-1$ 个石子($z-1 \ge L>0$),**保证左侧比右侧多$1$个石子**,就能回到 $Case$ $3$ 本身,递归证明即可 - * 若 $z=L$,则后手将最右堆拿完,根据 $L[i][j-1]$ 定义知此时局面必败 + * 若 $z=L$,则后手将最右堆拿完,根据 $L=left[i][j-1]$ 定义知此时局面必败 * 若 $0 @@ -178,7 +203,6 @@ $$ 同理可求 $R(i,j)$。 -回到原题,**先手必败当且仅当** $L[2][n]=a_1$ ,于是我们就做完啦! 时间复杂度 $O(n^2)$。 diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md b/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md index bb2fb98..2d86fae 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1322_2.md @@ -33,369 +33,143 @@ $1≤T≤10,1≤n≤1000,1≤a_i≤10^9$ 0 ``` -### 解题思路 -首先定义两个状态: -① $left[i][j]$ : 我在$[i,j]$这个区间的左边放上一堆数量是多少的石头能够让我先手必败. -② $right[i][j]$: 我在$[i,j]$区间的左边放上一堆数量是多少的石头能让我先手必败. - -$left[i][j]$ 的性质:**必然存在且唯一**. - -**证明**:假设在区间左边放上$L_1,L_2$ 数量的石堆都能使我必败,不妨设$L_1R -� -> -� -且RR -� -> -� -,那么后手可以在另一边取先手取得数量少一的数量,这样对于每次先手取完之后剩余X>R -� -> -� -的情况来说,后手都可以在另一边取. -取完之后X=R -� -= -� -,那么就对应情况1,后手把另一边的石堆取光 -取完之后X=R -� ->= -� -,那么后手在另一边取数量加一的数量,对于每次先手取的情况,后手都可以在另一边取. -取完之后XL -� -> -� -且L<=X=L -� ->= -� -,后手就在另一边取数量加一的石头 -取完之后X=L+1 -� ->= -� -+ -1 -,后手就在另一边取数量减一的石头 -取完之后X<=L -� -<= -� -,后手就把右边的石头取完 -取完之后XL -� -> -� -且X>R -� -> -� -, left[i][j]=X -� -� -� -� -[ -� -] -[ -� -] -= -� -4.1: -X>L>R -� -> -� -> -� -: -先手取左边: -取完之后X>L>R -� -> -� -> -� -,那么后手在右边取相同数量的石子 -取完之后X==L -� -== -� -,那么后手把右边的石堆取完 -取完之后RL>R -� -> -� -> -� -,那么后手在左边取相同数量的石子 -取完之后RR>L -� -> -� -> -� -: - -先手取左边: -取完之后X>R>L -� -> -� -> -� -,那么后手在右边取相同数量的石子 -取完之后LR>L -� -> -� -> -� -,那么后手就在左边取相同的数量的石子 -取完之后L<=X必然存在若干种办法一步可达必败局面。 + +若从最左边一堆中拿,因为假设原因,不可能变成必败局面,因为这样得到的局面仍形如 $A(x)$。 + + +左边拿没用,只能考虑从右边拿(即从$a_j$里拿): + +于是设 $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 > 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$个),矛盾,假设不成立,原命题成立。 + +#### 3、$left[i][j]$,$right[i][j]$必然唯一 + +**反证法**: +假设 $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)$ 均为必败局面,而 **第一个必败局面** 可以通过拿走左侧$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$呢? +答:博弈论的题目都是可以通过动态规划来递推的。 +为什么呢?你想啊,最终的胜利,肯定不是从天下直接掉下来的,是一步步取得的,也就是前序状态会直接影响后面的状态,所以 +> **博弈论 $\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]$ + +$left[i][j]$递推 + +`?[i j-1] 第j堆(石子个数x)` +则我们希望求的是假设$i\sim j$已经固定了,我们在左边放多少个可以使得`?[i j-1] 第j堆` 是必败的 +定义: +左边放$L$时,`L[i j-1]`必败 +右边放$R$时,`[i j-1]R`必败 + + +**$Q$:那为什么是定义先手必败,而不是先手必胜呢?** +答:因为上面证明过定义先手必败的动态规划数组,是肯定存在并且是唯一的,这样才能正常计算啊。 + +考虑三个问题: +- ① 初始值 +- ② 答案在哪 +- ③ 递推式 +> **注:答案在哪,并不是和递推式相关,而是和状态表示相关,一定要注意** + + +**① 初始值** +$\large L[i][i]=R[i][i]=a_i$ + +当只有一堆石子时($only$ $i$),我在这堆前面添加一堆,个数和这堆一样多,对于**两堆相同的石子**,**后手进行和先手对称的操作**,你咋干我就咋干,我拿完,你瞪眼~, **先手必败** + + +**② 答案在哪** +**先手必败** $\Leftrightarrow \ L[2][n]=a_1$ +> **解释**:如果$L[2][n]$与$a[1]$相等,就意味着本来挺好的$a[2] \sim a[n]$,结果,前面放上了一个$a[1]$,而加上的$a[1]$使得现在的局面必败,先手必败。 + + +**③ 递推式** +* 变化方法:从左侧拿走一些石子或者从右侧拿走一些石子 +![](https://cdn.acwing.com/media/article/image/2022/04/04/145584_d2d0424bb4-%E6%97%A0%E6%A0%87%E9%A2%98.jpg) + + + +### 六、实现代码 +```cpp {.line-numbers} +#include + +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; +} +``` \ No newline at end of file