「博弈&位运算」投票游戏
本题为1月7日22寒假集训每日一题题解
题目来源:(未知)
题面
题目描述
信奥班的同学总是这么无聊,他们现在喜欢玩一种投票游戏。
游戏规则如下,把参与游戏的同学们编号由1到N,然后他们开始投票,每个人只能投一票,只能是赞成或者反对,投票结果下来后如果赞成人数严格大于反对的人数,那么编号最小的那位同学便被踢出游戏,剩下的同学继续开始投票,直到投票僵持下来(比如剩两个人的时候一个人反对一个人赞成就会一直僵持下去),这时候游戏结束。
熊教准备了一些小红花,会把他们平均分给这些剩下的同学。每个同学都想得到最多的小红花,而且不想自己被踢出。给出同学个数N,问最后剩下的同学个数,假定信奥班的同学都无比机智。
输入
一行,一个整数N
输出
一行,一个整数表示剩下的同学个数。
样例输入
3
样例输出 Copy
2
提示
30% 1<=n<=50.
100% 1<=n<=5000.
思路分析
显然这是一道博弈论的题目,不过本题不同于二人博弈的规则,不好直接编写动态规划的代码,所以需要我们手动进行状态转移并寻找规律.
如果还剩一个人,显然他一直赞成即可不让自己被踢掉,属于僵持状态,此时答案为1.
如果当前还剩两个人,显然第一个人一直反对即可不让自己被踢掉,属于僵持状态,此时答案为2.
如果当前还剩三个人,无比机智的第二个人和第三个人会发现,如果把第一个人踢了,在人数减少的同时可以进入僵持状态,自己能稳定拿到更多的糖果,所以会联手一起赞成把第一个人踢掉,转移到还剩两人的僵持状态,此时答案为2.
如果当前还剩四个人,虽然无比机智的第三个人和第四个人会联手,企图把第一个和第二个人踢掉来达到人数更少的僵持状态,但是同样无比机智的第二个人会发现,一旦第一个人被踢了,那么就会进入并不僵持的三人状态,此时他一定会被联手踢掉,所以想要获得糖果,就必须和第一个人联手一起反对,在防止第一个被踢掉的同时,防止自己被踢掉.因此此时不会有任何一个人能够被踢掉,属于僵持状态,此时答案为4.
剩下同理分析,会发现只有当前人数是前一个僵持状态的2倍时(有足够的人联手反对),才能维持成一个新的僵持状态.也就是说,只有当前人数为2的非负整数幂时,才是僵持状态,而其他状态一定会转移到最接近的小于当前人数的僵持状态.
找到了上述的规律后,此题就转化成求最近的小于当前数的2的一个非负整数幂是多少了.要解决这个问题,可以直接枚举
2的倍数,直到大于当前数即可;也可以借助位运算
,由于2的一个非负整数幂的二进制只有一个1,所以只需要让当前数不断与上当前数减一,把低位的1全部消掉即可.
这里我采用的是效率更高的位运算
的写法.
参考代码
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast", "inline")
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
int n;
cin >> n;
// 不断与上当前数减一,消掉低位的1,直到只剩一个1(只剩一个1时,再与一次的结果就会是0,以此作为循环条件即可,注意位运算的优先级很低)
while (n & (n - 1))
{
n &= n - 1;
}
cout << n << "\n";
return 0;
}
"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德
这里是浙江理工大学22届ACM集训队的成员一枚鸭!
本文首发于博客园,作者:星双子,除了我自己的转载请注明原文链接:https://www.cnblogs.com/geministar/p/zstu22_1_7.html