题目链接:A-买爱心气球_2023河南萌新联赛第(五)场:郑州轻工业大学 (nowcoder.com)
题目描述
Alice 和 Bob 是一对竞技编程选手,他们路过了一家气球店,发现有 m 个大爱心气球和 n 个小爱心气球。他们决定玩一个游戏,游戏规则如下:
- Alice 先手拿球,两人轮流进行。
- 每个人在自己的回合只能选择一种类型的气球。
- 对于大爱心气球,每次拿取可以选择取 5 个、2 个或 1 个。
- 对于小爱心气球,每次拿取可以选择任意数量 (不含0个)。
游戏终止的条件是当所有的气球都被拿取完毕,最后一个球被拿取的人即为获胜者。
假设两人都足够聪明并采取最优策略,请问谁将获胜?
输入描述:
本题包含多组数据 第一行包含一个正整数 ,代表测试用例的组数。 对于每组数据: 输入一行包含两个正整数 。 数据保证 m 和 n 不同时为 0
输出描述:
对于每组数据: 输出一行一个字符串,如果 Alice 获胜,输出 "Alice" 否则如果 Bob 获胜,输出 "Bob" (输出不含引号)。
示例1
输入
3
3 1
3 3
5 2
输出
Alice
Alice
Bob
思路:
这个题是一个博弈的问题,看不出规律就可以使用SG来解决,首先不会SG的可以先去学一手SG,很简单。
最后的必胜的终态有两种:
(1)当m一次就可以被取完,n已经没有的时间,必胜
(2)当m取完以后,n还有剩余就可以一次取完,必胜
SG函数:
int sg(int m,int n){
if(mp.find({m,n}) != mp.end())//记忆化,会使的程序快很多
return mp[{m,n}];
set<int>st;//设立一个set来存他所有子状态的SG值
if(m == 1 || m == 2 || m == 5){//必胜终态
if(n == 0)return 1;
}
if(m == 0){//必胜终态
if(n != 0)return 1;
}
//模拟取气球的过程,找出其所有的子状态
for(int i = 1;i <= n;i++){
st.insert(sg(m,n - i));
}
if(m >= 5){
st.insert(sg(m - 5,n));
st.insert(sg(m - 2,n));
st.insert(sg(m - 1,n));
}
else if(m >=2){
st.insert(sg(m - 2,n));
st.insert(sg(m - 1,n));
}
else if(m >= 1){
st.insert(sg(m - 1,n));
}
//找到最小的没有出现的值
for(int i = 0;;i++){
if(st.count(i) == 0){
mp[{m,n}] = i;
return i;
}
}
}
然后就可以固定m,跑n,就会很神奇的发现,其实n只有在1和2的情况下会出现先手必败的状态,在找一下规律会发现当m自增的时间,这个先手必败状态出现的 n 的值是循环出现的,先出现在1,在出现在2,在没有必败,在出现在1,出现在2......
所以这个规律就相当的明显了,当 的时间是先手必败的状态