• 简单博弈论(Nim游戏)


    891. Nim游戏

    给定 n

    堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。

    问如果两人都采用最优策略,先手是否必胜。

    输入格式

    第一行包含整数 n

    第二行包含 n

    个数字,其中第 i 个数字表示第 i

    堆石子的数量。

    输出格式

    如果先手方必胜,则输出 Yes

    否则,输出 No

    数据范围

    1≤n≤105

    ,
    1≤每堆石子数≤109

    输入样例:

    1. 2
    2. 2 3

    输出样例:

    Yes
    

     * 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
     * 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
     * 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
     * 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
     *      用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
     *      因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以  S1^S2^...^Sj^...^Sn == Si;
     *      所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
     *      件矛盾),不成立;
     *      即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
     *      必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
     *
     * 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
     * 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
     * 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
     * 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
     *  证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
     *  所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
     *  =>  S1^S2^...^(Si^m)^Sj^...^Sn == 0
     *  =>  将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
     *  因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
     *  所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
     *   现在来证明 Si-(Si^m) > 0;
     *    证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
     *          一定是0;
     *          所以在最极端的情况下, Si 与 Si^m 的比较:
     *                          Si      Si^m
     *      第k位               1       0
     *      第k位前面的位置     1/0     与Si相同
     *      第k位后面的位置     1/0     不确定(但即便是 Si 取全0,Si^m取全1,两个数相
     *                                          减也一定满足大于0);
     *    得证!
     *  得证!

    1. /**
    2. * 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
    3. * 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
    4. * 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
    5. * 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
    6. * 用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
    7. * 因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以 S1^S2^...^Sj^...^Sn == Si;
    8. * 所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
    9. * 件矛盾),不成立;
    10. * 即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
    11. * 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
    12. *
    13. * 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
    14. * 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
    15. * 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
    16. * 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
    17. * 证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
    18. * 所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
    19. * => S1^S2^...^(Si^m)^Sj^...^Sn == 0
    20. * => 将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
    21. * 因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
    22. * 所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
    23. * 现在来证明 Si-(Si^m) > 0;
    24. * 证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
    25. * 一定是0;
    26. * 所以在最极端的情况下, Si 与 Si^m 的比较:
    27. * Si Si^m
    28. * 第k位 1 0
    29. * 第k位前面的位置 1/0 与Si相同
    30. * 第k位后面的位置 1/0 不确定(但即便是 Si 取全0,Si^m取全1,两个数相
    31. * 减也一定满足大于0);
    32. * 得证!
    33. * 得证!
    34. */
    35. #include
    36. using namespace std;
    37. int main()
    38. {
    39. int n;
    40. cin >> n;
    41. int res=0;
    42. while (n -- )
    43. {
    44. int val;
    45. cin >> val;
    46. res^=val;
    47. }
    48. if(res)
    49. puts("Yes");
    50. else
    51. puts("No");
    52. return 0;
    53. }

    893. 集合-Nim游戏

    给定 n

    堆石子以及一个由 k 个不同正整数构成的数字集合 S

    现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S

    ,最后无法进行操作的人视为失败。

    问如果两人都采用最优策略,先手是否必胜。

    输入格式

    第一行包含整数 k

    ,表示数字集合 S

    中数字的个数。

    第二行包含 k

    个整数,其中第 i 个整数表示数字集合 S 中的第 i 个数 si

    第三行包含整数 n

    第四行包含 n

    个整数,其中第 i 个整数表示第 i 堆石子的数量 hi

    输出格式

    如果先手方必胜,则输出 Yes

    否则,输出 No

    数据范围

    1≤n,k≤100

    ,
    1≤si,hi≤10000

    输入样例:

    1. 2
    2. 2 5
    3. 3
    4. 2 4 7

    输出样例:

    Yes
    

    /**
    1.Mex运算:

    设S表示一个非负整数集合.定义mex(S)为求出不属于集合S的最小非负整数运算,即:
    mes(S)=min{x};
    例如:S={0,1,2,4},那么mes(S)=3;

    2.SG函数

    在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,····yk,
    定义SG(x)的后记节点y1,y2,····yk的SG函数值构成的集合在执行mex运算的结果,即:
    SG(x)=mex({SG(y1),SG(y2)····SG(yk)})
    特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即 SG(G)=SG(s).

    3.有向图游戏的和

    设G1,G2,····,Gm是m个有向图游戏.定义有向图游戏G,他的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步.G被称为有向图游戏G1,G2,·····,Gm的和.
    有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数的异或和,即:
    SG(G)=SG(G1)xorSG(G2)xor···xor SG(Gm)

    以上定义是转载了一个dl的:
    作者:E.lena
    链接:https://www.acwing.com/solution/content/23435/
    来源:AcWing

    只要得到所有石堆的SG值,那么就和 Nim 游戏操作一样,将每堆石子的SG值当作 Nim游戏的
    每堆石堆的数量,那么每次都可以取连接该集合的任一个方向,单是要得到最优策略,还是要
    根据所有石堆的异或和来选择特定石堆的数量;

    给出Nim游戏的证明后再探讨Nim集合的正确性:

     * 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
     * 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
     * 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
     * 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
     *      用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
     *      因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以  S1^S2^...^Sj^...^Sn == Si;
     *      所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
     *      件矛盾),不成立;
     *      即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
     *      必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
     *
     * 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
     * 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
     * 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
     * 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
     *  证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
     *  所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
     *  =>  S1^S2^...^(Si^m)^Sj^...^Sn == 0
     *  =>  将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
     *  因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
     *  所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
     *   现在来证明 Si-(Si^m) > 0;
     *    证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
     *          一定是0;
     *          所以在最极端的情况下, Si 与 Si^m 的比较:
     *                          Si      Si^m
     *      第k位               1       0
     *      第k位前面的位置     1/0     与Si相同
     *      第k位后面的位置     1/0     不确定(但即便是 Si 取全0,Si^m取全1,两个数相
     *                                          减也一定满足大于0);
     *    得证!
     *  得证!

    因为我们在Nim游戏的证明中证明出了:
    如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
    那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
    能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;且Si-(Si^m) > 0,所以在Nim集合中,
    我们能得出,SG1^SG2^...^SGi^...^SGn !=0 ==m 的时候,我们可以选择第i个集合里选择第
    SGi-(SGi^m) 条路线,(此路线一定存在,因为SGi-(SGi^m) 所以必定存在 SGi-(SGi^m) 属于 [0,SGi);

    SG1^SG2^...^SGi^...^SGn == 0,就不消证明,先手必输;
    */

    1. /**
    2. 1.Mex运算:
    3. 设S表示一个非负整数集合.定义mex(S)为求出不属于集合S的最小非负整数运算,即:
    4. mes(S)=min{x};
    5. 例如:S={0,1,2,4},那么mes(S)=3;
    6. 2.SG函数
    7. 在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,····yk,
    8. 定义SG(x)的后记节点y1,y2,····yk的SG函数值构成的集合在执行mex运算的结果,即:
    9. SG(x)=mex({SG(y1),SG(y2)····SG(yk)})
    10. 特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即 SG(G)=SG(s).
    11. 3.有向图游戏的和
    12. 设G1,G2,····,Gm是m个有向图游戏.定义有向图游戏G,他的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步.G被称为有向图游戏G1,G2,·····,Gm的和.
    13. 有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数的异或和,即:
    14. SG(G)=SG(G1)xorSG(G2)xor···xor SG(Gm)
    15. 以上定义是转载了一个dl的:
    16. 作者:E.lena
    17. 链接:https://www.acwing.com/solution/content/23435/
    18. 来源:AcWing
    19. 只要得到所有石堆的SG值,那么就和 Nim 游戏操作一样,将每堆石子的SG值当作 Nim游戏的
    20. 每堆石堆的数量,那么每次都可以取连接该集合的任一个方向,单是要得到最优策略,还是要
    21. 根据所有石堆的异或和来选择特定石堆的数量;
    22. 给出Nim游戏的证明后再探讨Nim集合的正确性:
    23. * 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
    24. * 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
    25. * 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
    26. * 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
    27. * 用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
    28. * 因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以 S1^S2^...^Sj^...^Sn == Si;
    29. * 所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
    30. * 件矛盾),不成立;
    31. * 即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
    32. * 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
    33. *
    34. * 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
    35. * 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
    36. * 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
    37. * 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
    38. * 证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
    39. * 所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
    40. * => S1^S2^...^(Si^m)^Sj^...^Sn == 0
    41. * => 将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
    42. * 因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
    43. * 所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
    44. * 现在来证明 Si-(Si^m) > 0;
    45. * 证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
    46. * 一定是0;
    47. * 所以在最极端的情况下, Si 与 Si^m 的比较:
    48. * Si Si^m
    49. * 第k位 1 0
    50. * 第k位前面的位置 1/0 与Si相同
    51. * 第k位后面的位置 1/0 不确定(但即便是 Si 取全0,Si^m取全1,两个数相
    52. * 减也一定满足大于0);
    53. * 得证!
    54. * 得证!
    55. 因为我们在Nim游戏的证明中证明出了:
    56. 如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
    57. 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
    58. 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;且Si-(Si^m) > 0,所以在Nim集合中,
    59. 我们能得出,SG1^SG2^...^SGi^...^SGn !=0 ==m 的时候,我们可以选择第i个集合里选择第
    60. SGi-(SGi^m) 条路线,(此路线一定存在,因为SGi-(SGi^m)
    61. 所以必定存在 SGi-(SGi^m) 属于 [0,SGi);
    62. SG1^SG2^...^SGi^...^SGn == 0,就不消证明,先手必输;
    63. */
    64. #include
    65. #include
    66. #include
    67. using namespace std;
    68. const int maxn = 10010;
    69. int hs[maxn],f[maxn];
    70. int m;
    71. int SG(int val)
    72. {
    73. if(hs[val]!=-1) //如果val块石子的石堆处理过,直接返回
    74. return hs[val];
    75. set<int> st;
    76. for(int i=0;i
    77. {
    78. int sum=f[i];
    79. if(val>=sum)
    80. st.insert(SG(val-sum));
    81. }
    82. for(int i=0; ;++i)
    83. if(!st.count(i)) //只要有最小不等于该集合的非负整数,就赋给hs[val];
    84. return hs[val]=i;
    85. }
    86. int main()
    87. {
    88. cin >> m;
    89. for(int i=0;i
    90. cin >> f[i];
    91. memset(hs,-1,sizeof(hs)); //将所有集合都初始化为-1,表示未被处理
    92. int n;
    93. cin >> n;
    94. int res=0;
    95. for(int i=0;i
    96. {
    97. int val;
    98. cin >> val;
    99. res^=SG(val);
    100. }
    101. if(res) //如果异或和不为0
    102. puts("Yes");
    103. else
    104. puts("No");
    105. return 0;
    106. }

  • 相关阅读:
    开放词汇视觉定位 OV-VG: A Benchmark for Open-Vocabulary Visual Grounding 论文笔记
    数组名和&数组名
    【华为OD统一考试B卷 | 100分】单词接龙(C++ Java JavaScript Python)
    05 | 基础篇:某个应用的CPU使用率居然达到100%,我该怎么办?笔记
    Paillier算法简介
    Mac OS 使用ScreenCaptureKit进行窗口抓取和系统声音抓取
    2025~《数据结构》试题~考研
    NLP(六十七)BERT模型训练后动态量化(PTDQ)
    使用 RAG、Langchain 和 Streamlit 制作用于文档问答的 AI 聊天机器人
    c语言,c++,java和python这些语言有何区别?编译型编程语言编译语言,解释型编程语言解释型语言
  • 原文地址:https://blog.csdn.net/qq_51825761/article/details/126352961