文章目录
题目描述
题目解析
假设先手者名为
A
,后手者名为B
。
由于只关注是否取完 和 数字和是否 %3==0
,我们把这题要取的数字分为三种:
-
%3==0
的数字(0类型
):只要不是第一次取数,并不会对整个数字的和模产生任何影响。 -
%3 ==1
的数字(1类型
):一旦数字和的模已经是2,则游戏结束,只有数字和的模为1才能继续游戏。 -
%3==2
的数字(2类型
):同上个类型,只有数字和的模与当前数字的模相同才能继续游戏。
根据以上分析:
一、若 0类型
的数字为偶数个,由于 A
先手必不可能选择这类数字,所以 0类型
的数字不会对结果产生任何影响。
- 当
1类型
和2类型
的数字个数都不为0。则A
只要先手选到数字更少的那一类,那么就必胜!为什么必胜呢?举个例子假设有 1 1 2 2 2这样的数字,那么A
选了1后,B
就只能跟着选1,而之后A
就只能选2。很明显A
除了第一次选了偏少的那一堆的一个元素以外,其余都是取的偏多的那一堆元素里的数,而B
只能取偏少的那一堆元素里的数。故总结出整个取数过程除了A
第一次选择,实际上就是一个对拼消耗的过程,拿着资源数量更多的A
和拿着资源数量更少的B
进行对拼消耗,最先消耗完的肯定就是B
了,则此时B
就只能*选择A
的那一堆使得和为3的倍数了,最终A
获胜。 - 当
1类型
或者2类型
的数字个数为0,那么无论如何都无法逃离A
输掉的命运。如果个数不为0的数字个数大于3个,A
先手取数,故A
一定是第三个取到它的,A
输,而其他情况则因为数字取完而输掉比赛。
二、若 0类型
的数字个数为奇数个,同样由于 A
不可能先手取这类数字,但这次由于这类数字是奇数个,可以多出一个来用于替 B
挡灾。
- 具体如何使用这个来挡灾呢?
我们先假设1类型
和2类型
的个数是相等的,此时我们的A
无论以谁起手都是一样的,假设以1类型
的情况起手,按理来说,接下来A
都是通过消耗2类型
的个数来打消耗战,但此时B
有了额外的一个0类型
的替身,所以B
这一轮可以选择不跟A
,而选择0类型
,那么从此A
就只能选择1类型
去进行消耗了,于是身份便互换了,假设1类型
和2类型
的数字数量开始是 N ,那么这身份互换后的结果就是:拿着数量为N-1的1类型
的A
与拿着数量更多的2类型
的B
进行对拼消耗,最终的结果肯定是B
胜。
根据以上互换身份的原理, A
在这种情况下,只有选数量多的才能有一线生机,而且至少要比 B
多三份物资,原因在于:
- 发生转化的过程还是要消耗掉 1 个。
- 想要让
A
赢,除了让B
的M
个物资消耗完以外,A
自身还需要留下一份物资供B
产生3的倍数。 - 此时的对拼消耗过程中
A
处于先手消耗,想要轮到B
的回合则永远会多消耗一个物资,如 A:2 2 2,B:1 1,对拼过程中明显无法回到B就消耗完了。
假设此时 A
选择的这个类型有 N
个,对方的有 M
个,故只要满足
N
−
3
>
=
M
N-3>=M
N−3>=M 则 A
必胜,否则还是 B
胜。
因为上面的 N 和 M 所代表的只是 1类型
和 2类型
中数量多和少的类型。故放入题中需要满足的就是 1类型
和 2类型
之间的数量差大于等于3。
解题代码
class Solution {
public:
bool stoneGameIX(vector<int>& stones) {
int cnt[3]{};
for (int val: stones) {
cnt[val%3]++;
}
if (cnt[0] % 2 == 0) {//TODO 0类型为偶数的情况
return cnt[1] >= 1 && cnt[2] >= 1;
}
return cnt[1] - cnt[2] >= 3 || cnt[2] - cnt[1] >= 3;//TODO 0类型为奇数的情况
}
};