「博弈&位运算」投票游戏

本题为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;
}

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

posted @ 2023-01-09 15:57  星双子  阅读(43)  评论(0编辑  收藏  举报