当前位置:   article > 正文

博弈论基础模板

博弈论基础模板

博弈论(1) - Nim游戏

  1. #include <iostream>
  2. using namespace std;
  3. // 先手必胜状态:可以走到某一个必败状态
  4. // 先手必败状态:走不到任意一个必败状态
  5. /*
  6. 给定n堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
  7. 问如果两人都采用最优策略,先手是否必胜。
  8. 输入格式
  9. 第一行包含整数n。
  10. 第二行包含n个数字,其中第i个数字表示第i堆石子的数量。
  11. 输出格式
  12. 如果先手方必胜,则输出"Yes"。
  13. 否则,输出"No"。
  14. 数据范围
  15. 1 ≤ n ≤ 10^5,
  16. 1 ≤ 每堆石子数 ≤ 10
  17. */
  18. // 定理:如果石子堆分别有a1,a2,a3,……,an, 若 a1^a2^a3^……^an = 0 的话,先手必败;不等于0,先手必胜
  19. /* 证明:先假定 a1 ^ a2 ^ a3 ^ …… ^ an = x (x != 0) 为先手必胜状态,
  20. a1 ^ a2 ^ a3 ^ …… ^ an = 0 为先手必败状态
  21. 证明:(1)通过改变a1~an中某个数值,一定可以将先手必胜状态转换为先手必败状态
  22. (2)改变a1-an中某个值,一定会将先手必败状态转换为先手必胜状态
  23. (1) x的二进制中最高位在第k位,则a1-an中必定存在一个数 ai 的第k位为1
  24. 则 ai^x < ai (把ai的第k位变为0了)
  25. 在 ai 堆中拿走 ai-(ai^x) 个石子,此时原ai堆石子数量为(ai^x)
  26. 拿完之后各石堆石子数量为:a1, a2, a3, ……, ai^x, ……, an
  27. 将它们异或起来:a1 ^ a2 ^ …… ^ (ai ^ x) ^ …… ^ an = x ^ x = 0
  28. 得证
  29. (2) 改变某个值ai,让其变为 aii
  30. 假设 a1 ^ a2 ^ …… ^ ai ^ …… ^ an = 0
  31. 又因为 a1 ^ a2 ^ …… ^ aii ^ …… ^ an = 0
  32. 上面2式子左边异或左边,右边异或右边,得到:ai ^ aii = 0,即 ai = aii
  33. */
  34. int main()
  35. {
  36. int n;
  37. int res = 0;
  38. cin >> n;
  39. while (n--)
  40. {
  41. int x;
  42. cin >> x;
  43. res ^= x;
  44. }
  45. if (res != 0)
  46. {
  47. cout << "Yes" << endl;
  48. }
  49. else
  50. {
  51. cout << "No" << endl;
  52. }
  53. return 0;
  54. }

博弈论(2) - 台阶Nim游戏

  1. #include <iostream>
  2. using namespace std;
  3. // http://t.csdn.cn/3Dks1
  4. int main()
  5. {
  6. int n;
  7. int res = 0;
  8. cin >> n;
  9. for (int i = 1; i <= n; i++)
  10. {
  11. int x;
  12. cin >> x;
  13. if (n % 2 == 1)
  14. {
  15. res ^= x;
  16. }
  17. }
  18. if (res != 0)
  19. {
  20. cout << "Yes" << endl;
  21. }
  22. else
  23. {
  24. cout << "No" << endl;
  25. }
  26. return 0;
  27. }

博弈论(3) - 集合Nim游戏

  1. #include <iostream>
  2. #include <unordered_set>
  3. using namespace std;
  4. /*
  5. 给定n堆石子以及一个由k个不同正整数构成的数字集合S。
  6. 现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合S,最后无法进行操作的人视为失败。
  7. 问如果两人都采用最优策略,先手是否必胜。
  8. 输入格式
  9. 第一行包含整数k,表示数字集合S中数字的个数。
  10. 第二行包含k个整数,其中第i个整数表示数字集合S中的第i个数si。
  11. 第三行包含整数n。
  12. 第四行包含n个整数,其中第i个整数表示第i堆石子的数量hi。
  13. 输出格式
  14. 如果先手方必胜,则输出"Yes"。
  15. 否则,输出"No"。
  16. 数据范围
  17. 1≤n,k≤100,
  18. 1≤si,hi≤10000
  19. */
  20. const int M = 10010; // 集合中元素最大有多大、石堆中石子的数量
  21. const int N = 110; //集合有多少个元素、有多少个石堆
  22. int k, n;
  23. int s[N]; // 存储集合中元素元素值
  24. int sg[M]; // 存储每个值的SG值
  25. /*
  26. mex运算:设S表示一个非负整数集合。定义mex(S)为求出不属于集合S的最小非负整数的运算,即:mex(S) = min(x), x属于自然数,且x不属于S
  27. 如 mex({3, 4, 5}) = 0
  28. */
  29. int mex(unordered_set<int>& S)
  30. {
  31. for (int i = 0;; i++)
  32. {
  33. if (S.count(i) == 0)
  34. {
  35. return i;
  36. }
  37. }
  38. }
  39. /*
  40. SG函数
  41. 在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,..yk,
  42. 定义 SG(x)为x的后继节点y1,y2,....yk的SG函数值构成的集合再执行mex(S)运算的结果即:SG(x) = mex((SG(y1), SG(y2). .., SG(yk)))
  43. 定义终点状态的SG为0
  44. 特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即SG(G) = SG(s)。
  45. 设G1、G2、……、Gm为m个有向图游戏,它的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步。(在本题中,每个石堆对应一个有向图,要求出每个石堆起点的SG值)
  46. G被称为有向图G1、G2、……、Gm的和。有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数值(起点s的SG函数值)的异或和
  47. 即:SG(G) = SG(G1) ^ SG(G2) ^ …… ^ SG(Gm)
  48. 定理: 有向图游戏的某个局面必胜,当且仅当该局面对应节点的SG函数值大于0。
  49. 有向图游戏的某个局面必败,当且仅当该局面对应节点的SG函数值等于0。
  50. */
  51. int SG(int x)
  52. {
  53. // 如果 x值 的SG值被算过,直接返回
  54. // 通过sg数组,避免重复运算
  55. if (sg[x] != -1)
  56. {
  57. return sg[x];
  58. }
  59. // x值的SG值没有被算过,根据定义计算
  60. unordered_set<int> S;
  61. for (int i = 0; i < k; i++)
  62. {
  63. // 如果元素小于等于x才可以计算
  64. if (s[i] <= x)
  65. {
  66. // (x - s[i]) 大于等于 0,需要插入它的SG值
  67. if (sg[x - s[i]] != -1)
  68. {
  69. // 如果(x - s[i])的SG值已经被算过,直接插入
  70. S.insert(sg[x - s[i]]);
  71. }
  72. else
  73. {
  74. // 未被算过,计算后插入
  75. S.insert(SG(x - s[i]));
  76. }
  77. }
  78. }
  79. // 将计算出来 x的SG值 赋给数组并返回
  80. sg[x] = mex(S);
  81. return sg[x];
  82. }
  83. int main()
  84. {
  85. cin >> k; // 集合中元素个数
  86. for (int i = 0; i < k; i++)
  87. {
  88. cin >> s[i];
  89. }
  90. memset(sg, -1, sizeof(sg)); // 如果 sg[i] 为-1,则说明 数i 的 SG值 还未被算过
  91. cin >> n; // 石堆数
  92. int res = 0; // 存储结果值
  93. for (int i = 0; i < n; i++)
  94. {
  95. int x; // 当前石堆中石子数
  96. cin >> x;
  97. res ^= SG(x);
  98. }
  99. if (res != 0)
  100. {
  101. cout << "Yes" << endl;
  102. }
  103. else
  104. {
  105. cout << "No" << endl;
  106. }
  107. return 0;
  108. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/471375
推荐阅读
相关标签
  

闽ICP备14008679号