diff --git a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md index a72bd71..43dc7e6 100644 --- a/TangDou/AcWing_TiGao/T5/GameTheory/1321.md +++ b/TangDou/AcWing_TiGao/T5/GameTheory/1321.md @@ -51,52 +51,117 @@ NO 必败态 $\Rightarrow$ 选择任何路线 $\Rightarrow$ 必胜态 -### 三、本题题解 +### 三、简单情况 +为什么会想到讨论简单情况呢?我们来思考一下:如果某一堆石子只有$1$个,随着我们执行拿走$1$个的操作,它的堆就没了,这样石子个数变了,堆数也变了,两个变量,问题变复杂了,我们上来就想难题,怕是搞不定。 -先来考虑 **简单情况**:所有堆的石子个数 $>1$ +既然这样,我们就思考一下 **子空间** :只考虑所有个数大于等于$2$的那些堆,其它可能存在石子数等于$1$的,等我们想明白这个简单问题再研究扩展的事,由易到难。 -设 $b$ = 堆数 + 石子总数 $− 1$ +同时,我们需要思考博弈的胜负与什么因素相关呢?因为只有两种操作:**拿走一个石子、合并两堆**,很显然,**两个关键因素:石子个数、堆数** -可以推出: **先手必胜 $\Leftrightarrow$ $b$是奇数** +同时,两个操作同一时间只能执行一个,所以可以理解为拿走一个石子对结果影响一下,合并两堆石子对结果也是影响一下,初步考虑应该堆个数与石子总数的加法关系相关。 -**证明**:考虑以下结论: -$α$. 任意一个是奇数的情况 **可以** 被转换成一个偶数的情况 (一定存在一个偶数后继) -$β$. 任意一个是偶数的情况 **一定** 会被转换成一个奇数的情况 (所有后继都是奇数)。 +**子空间:当每堆的石子个数都是大于等于$2$时** +
设$b$ = 堆数 + 石子总数 - $1$
+
结论:$b$是奇数⟺先手必胜,$b$是偶数⟺先手必败
-先来考虑结论 $α.$ -- 如果堆数 $>1$,可以合并其中两堆石子,这样 $b$ 就会变成偶数 -- 如果堆数 $=1$,就可以拿掉 $1$ 个石子,这样 $b$ 也会变成偶数 +**证明:** -再来考虑结论 $β$. -- 合并其中的两堆石子:显然,$b$ 会变成奇数 -- 取了一个石子: - (1) 取得堆中石子个数 $>2$:石子个数 $− 1$,$b$ 会变成奇数 - (2) 取得堆中石子个数 $=2$:石子个数变成了 $1$(继续分情况讨论): - 1) 只有一堆:$b$ 是奇数,对手必胜 - 2) 多于一堆:$b$ 是奇数,对手可以把剩下的一个石子放到其他的堆里面去,对手同样必胜 +1、边界:当我们只有一堆石子且该堆石子个数为$1$个时,$b=1$,先手必胜。 -这样,我们成功的证明除了: **先手必胜 $\Leftrightarrow $ ($b$ 是奇数)** +2、当$b$为奇数,一定可以通过某种操作将$b$变成偶数 +* 如果堆数大于$1$,合并两堆让$b$变为偶数 +* 如果堆数等于$1$,从该堆石子中取出一个就可以让$b$变为偶数 +3、当$b$为偶数,无论如何操作,$b$都必将变为奇数 +* 合并两堆,则$b$变为奇数 +* 从某一堆中取走一个石子: + * 若该堆石子个数大于$2$,则$b$变为奇数,且所有堆石子数量严格大于$1$ + * 若该堆石子个数等于$2$,取一个石子后,$b$变为奇数,该堆石子个数变为$1$个,此时就再是子空间范围内了,因为出现某堆的石子个数为$1$,而不是每一堆都大于等于$2$了!需要继续分类讨论: -接下来考虑一般情况:有些堆的石子个数可能 $=1$ -假设石子个数 $=1$ 的堆数为 $a$,$b$ 仍然表示剩余石子的 **操作数**,即 (堆数 $+$ 石子总数 $− 1$) -把所有石子分成两个区域,一区域的数量 $=1$ (对应 $a$),二区域的数量都 $>1$ (对应 $b$). +#### 特殊情况 +此时为了保证所有堆的石子个数大于$1$,**足够聪明的对手** 可以进行的操作分为两类: + ① 如果只有这一堆石子,此时 **对手必胜** + ② 如果有多堆石子,可以将这一个石子合并到其他堆中,这样每对石子个数都大于$1$ + + **$Q$:对手为什么一定要采用合并的操作,而不是从别的堆中取石子呢?** + 我来举两个简单的栗子: + + * **只有一堆石子** + 石子个数是$2$个。你拿走一个,对手直接拿走另一个,游戏结束,**对手赢了**!你也是足够聪明的,你会在这种情况下这么拿吗?不能吧~,啥时候可能遇到这个情况呢?就是你被 **逼到** 这个场景下,也就是一直处于必败态! + + * **两堆石子** + 每堆石子个数是$2$个。**我是先手**,可以有两种选择: + + (1)、从任意一堆中拿走$1$个, 现在的局面是$\{2,1\}$ + $$\large 后手选择(对手) \Rightarrow + \left\{\begin{matrix} + 从2中取一个 & \Rightarrow \{1,1\} & \Rightarrow + \large \left\{\begin{matrix} + 先手合并 \Rightarrow \{2\}& 剩下一个一个取,先手胜 \\ + 先手后手一个一个取 \Rightarrow 先手败 & + \end{matrix}\right. + \\ + 从1中取一个& \Rightarrow \{2,0\} & 剩下一个一个取,先手败\\ + 合并两堆 & \Rightarrow \{3\} & 剩下一个一个取,先手胜 \\ + \end{matrix}\right. + $$ + 指望对手出错我才有赢的机会,人家要是聪明,我就废了! + + 我是先手,我肯定不能把自己的命运交到别人手中!我选择合并两堆,这样我保准赢! + + (2)、把两堆直接合并,现在的状态$\{4\}$ + 这下进入了我的套路,你取吧,你取一个,我也取一个;你再取一个,我也再取一个,结果,没有了,**对手必败**。 + + 上面的例子可能不能描述所有场景,我现在$b$是奇数,我在必胜态,我不会让自己陷入到$b$可能是偶数的状态中去,如果我选择了 + - 合并操作减少$1$个堆 + - 拿走操作减少$1$个石子 + 都会把$b-1$这个偶数态给对方 -那么可以写出以下转移: + 我不会傻到一个操作,即可能造成堆也变化,让石子个数也变化,这样就得看对方怎么选择了,而他还那么聪明,我不能犯这样的错误。 -$f(a,b)$: - -① 从 $a$中取 $1$个 $→f(a−1,b)$ -② 从 $b$中取 $1$个 $→f(a,b−1)$ -③ 合并 $b$中的 $2$个 $→f(a,b−1)$ -④ 合并 $a$中 $2$个 ($b$堆石子数 $+2$,堆数 $+1$) $→f(a−2,b+3)$ -⑤ 合并 $a$中 $1$个 $b$中 $1$个 ($b$堆石子数 $+1$, $a$个数 $−1$)$→f(a−1,b+1)$ +### 四、本题情况 +本题中可能存在一些堆的石子个数等于$1$: +* 假设有$a$堆石子,其中每堆石子个数为$1$ +* 剩余堆的石子个数都严格大于$1$ + +根据这些数量大于$1$的堆的石子可以求出上述定义出的$b$,我们使用$f(a, b)$表示此时先手必胜还是必败,因为博弈论在本质上是可以递推的,我们可以想出起点,再想出递推关系,就可以递推得到更大数据情况下的递推值,也就是博弈论本质上是$dp$。 + +
+ +相关疑问 +$Q1:$**情况**$3$**为什么是两个表达式?** +答: +①当右侧存在时,合并左边两堆石子,则右侧多出一堆石子,并且,石子个数增加$2$,也就是$b+=3$ + +②当右侧一个都没有的时候,左边送来了一堆,两个石子,按$b$的定义,是堆数+石子个数$-1=2$,即$b+=2$ + +$Q2$:**为什么用一个奇数来描述简单情况的状态,而不是用偶数呢?** +答:因为要通过递推式进行计算,最终的边界是需要我们考虑的: + +- 如果用奇数,那么边界就是$b=1$,表示只有$1$堆,石子数量只有$1$个,此时当然必胜。 +- 如果用偶数,比如边界是$b=0$,表示目前$0$堆,$0$个石子,这都啥也没有了,还必胜态,不符合逻辑,说不清道不明。 -#### $Code$ +- 那要是不用$b=0$做边界,用$b=2$呢?表示只有$1$堆,石子数量只有$1$个,这个应该也是可以,但没有再仔细想了。 + +$Q3:$**情况**$2$**从右边取一个石子,如果此时右侧存在某一堆中石子个数是$2$,取走$1$个后,变成了$1$,不就是右侧减少了一个堆,减少了两个石子,即$b-=3$;同时,此堆石子个数变为$1$,左侧个数$a+=1$,为什么没有看到这个状态变化呢?** + +答:这是因为聪明人不会从右侧某个石子数量大于$2$的堆中取走石子! + +看一下 **讨论简单情况** 中第$3$点后面的 **特殊情况**: + - 如果右侧只有一堆,石子数量为$2$,拿走$1$个,剩$1$个,一堆一个,对方必胜,此为必败态 + + - 如果右侧大于一堆,某一堆只有$2$个石子,拿走$1$个,剩$1$个,对手足够聪明,会采用右侧两堆合并的办法,此时 石子数量减$1$,堆数减$1$,对$b$的影响是减$2$,对$b$的奇偶性没有影响,换句话说,如果你现在处在必败态,你这么整完,还是必败态 + + +### 五、时间复杂度 +这里因为$a$最大取$50$,$b$最大取$50050$,因此计算这些状态的计算量为$2.5×10^6$,虽然有最多$100$次查询,但是这些状态每个只会计算一遍,因此不会超时。 + + +### 六、实现代码 ```cpp {.line-numbers} #include using namespace std; @@ -161,4 +226,72 @@ int main() { } return 0; } -``` \ No newline at end of file +``` + +思路: 我们可以简单猜想当总操作数 +∑ +a +[ +i +] ++ +n +− +1 + 是奇数的时候,先手必胜。 但事实并非如此,有些情况并不成立 。 我们可以发现,当n是1的时候,这样结论显然成立。 +另外,我们还可以总结出当没有数量为1的石子堆时,这个结论也是成立的。 下面我们先来证明这个结论。 + +猜想: 当没有数量为1的堆,上面和是奇数的时候,先手必胜,如果是偶数,先手必败。 + +1. 先证明奇数必胜,对于先手来说,如果n>1,那么只要选两堆合并, 那么总操作数变成偶数,n=1明显只能选择减少1操作,后手还是偶数。对于后手来说,无论他是减少1,还是合并操作,留下的总操作数一定还是奇数。 + +对于某些读者来说,可能会问,如果后手把某个2变成1,先手该怎么办,其实这个很容易操作,如果堆数超过1,先手一定选择合并这个数量为1的堆,如果只有一堆了而且还是1,明显先手必胜了。 所以,先手总是有办法让 + +后手必败(操作数为偶数的局面),后手无论怎么走,都会让先手必胜(变成操作为奇数的局面),所以我们证明成立。 + +2. 在上面,我们也证明了当操作数是偶数的时候,先手是必败的。 + + + +接下来我们考虑有数量为1的堆的时候的情况,这个情况比较复杂,因为如果某个人把1减少1,那么这个堆同时也消失了,相当于操作数减少了2。 + +由于数据规模不是很大,我们采用了动态规划的思想,用记忆化搜索来实现情况。 + +f +[ +a +] +[ +b +] +表示当数量为1的堆有a个,剩下的堆的操作数是b的时候,先手是必胜还是必败, +f +[ +a +] +[ +b +] += +1 +表示必胜,否则必败。b中不会出现数量为1的堆,除非只剩下一个堆了。 + +情况1: 当a>=2的时候,合并两个数量为1的堆,这样就让b这边的操作数增加了2+(b>0),因为如果原来b大于0,相当于操作数增加了3,否则b原来是0,那么操作是只增加2. + +情况2: 和a>0 并且b>0 的时候,我们可以合并一个数量为1的堆和b中一个数量不为1的堆,那么a减少1,b增加1 + +情况3:b>=2,我们合并b里面的两个堆或者减少1,无论哪种,都是让b里面的操作数减少1。 + +情况4: 当a>0的时候,我们可以减少一个数量为1的堆,这样a就减少1,b不变。 + + + +另外有一种非常特殊的情况,就是b等于1了,刚才我们说了,b里面不会出现数量为1的堆,除非只剩下一个堆了。因为b里面只要堆的数量超过1,就一定可以用合并超过替代减少1操作,这样是等价的。 + +除非b里面只有一个堆了,那么我们就只能不断减少1了。 + +所以当b是1的时候,实际我们求的问题应该变成f[a+1][0] + + + +以上内容是我自己的一个总结,网上有些题解感觉写的不是很清楚,我自己理了一下思路,重新写了一个题解。 \ No newline at end of file