【题解】博弈论习题

学习链接:
https://blog.csdn.net/jk_chen_acmer/article/details/82082653
https://blog.csdn.net/dgq8211/article/details/7602807
https://blog.csdn.net/YangHao5/article/details/88972326

取石子游戏 2
有一种有趣的游戏,玩法如下:

玩家:2人;

道具: N \rm{N} N堆石子,每堆石子的数量分别为 X \rm{X} X1, X \rm{X} X2, …… X \rm{X} Xn;

规则:

  1. 游戏双方轮流取石子;
  2. 每人每次选一堆石子,并从中取走若干颗石子(至少取1颗);
  3. 所有石子被取完,则游戏结束;
  4. 如果轮到某人取时已没有石子可取,那此人算负。
  5. 假如两个游戏玩家都非常聪明,问谁胜谁负?

定理

NIM博弈先手必胜,当且仅当 X \rm{X} X1 ^ X \rm{X} X2 ^ … ^ X \rm{X} Xn ≠ 0

#include <cstdio> 
//特殊情况:当全为0时,必败态,此时异或结果为0
//保持异或结果为零,最后每堆石子都变为零 
//异或结果取决于每位上1的个数(奇数为1,偶数为0)
//1.必胜态:异或结果不为0,不妨设为k,找出二进制下,在 k的有效最高位为 1 的一堆石子,改变它使k=0
//(此时小于原来个数,因为最高位由一变零,后面改变都不超过这一位)即可 
//2.必败态:异或结果为0,因为是必胜态的下一步操作 
const int MAXN = 1e5 + 5;
int n, x, ans;
int main() {
    
    
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
    
    
        scanf("%d", &x);
        ans ^= x;
    }
    if (ans == 0)
        printf("lose");
    else
        printf("win");
}

「SEERC2019」Absolute Game

题目描述

Alice 和 Bob 在玩一个游戏。Alice 有一个包含 n \rm{n} n 个整数的数列 a \rm{a} a,Bob 有一个包含 n \rm{n} n个整数的数列 b \rm{b} b。每一回合中,玩家需要从他的数列中删去一个数字。玩家轮流进行回合,Alice 先手。

当两个数列中都只剩下一个数字的时候,游戏结束。令 Alice 的数列剩下的数字为 x \rm{x} x,Bob 的数列剩下的数字为 y \rm{y} y。Alice 想要最大化 x \rm{x} x y \rm{y} y之差的绝对值,而 Bob 想最小化这个值。两个玩家都以最优策略游戏。

请算出游戏结束时的结果。

分析:对A或B来说,均有两种选择:维护最优值或去掉最差值。每个人只会从自己最优的角度去游戏

  1. 首先假设A的策略是去掉最小值,则对 a \rm{a} a i \rm{i} i=1~n找到 b \rm{b} b j \rm{j} j使值最小。A会去掉n组中最小一组的 a \rm{a} a i \rm{i} i
  2. B有两种选择:
  • 维护最小值,因为这n组已经最小了,每个 a \rm{a} a i \rm{i} i对应一个最小值,A只能去掉n-1组,剩下一组是对B而言的最优结果
  • 将每个b配对去掉最大值,这样并不一定使B最终结果最小,因为它没有考虑A的策略,A至少去掉n-1组最小,很可能剩下一组最小所对应的b被B自己删去了,这样的策略不如前者
  1. 所以A每次会删去这n组中最大值,B每次会删去没有被a“匹配”到的b(因为b的数量比a多1,一定有剩余),答案就是n组最小中的最大值

那么为什么A不会维护最大值呢?因为B一定不会删去这n组最小值,假如A不删,最小值就会留到最后,这对A不利,因为结果不如前者

所以,本题思路是分别分析A和B,但彼此要考虑对方的策略,才能使自己的策略最优。这就是博弈论的思想

//frist try
//错误代码,对B不利的策略
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 1005;
int n, a[MAXN], b[MAXN], t, tot, _min, _max;
int main() {
    
    
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
    sort(a + 1, a + 1 + n);
    sort(b + 1, b + 1 + n);
    while (n != 1) {
    
    
        t = 0, _min = 1e9;
        for (int i = 1; i <= n; i++) {
    
    
            tot = 1e9;
            for (int j = 1; j <= n; j++) tot = min(tot, abs(a[i] - b[j]));
            if (tot < _min)
                t = i, _min = tot;
        }
        for (int i = t; i <= n - 1; i++) a[i] = a[i + 1];
        t = 0, _max = -1;
        for (int i = 1; i <= n; i++) {
    
    
            tot = max(abs(b[i] - a[1]), abs(b[i] - a[n - 1]));
            if (tot > _max)
                t = i, _max = tot;
        }
        for (int i = t; i <= n - 1; i++) b[i] = b[i + 1];
        n--;
    }
    printf("%d", abs(a[1] - b[1]));
}
//second try
//双方最优策略
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 1005;
int n, a[MAXN], b[MAXN], _min, _max = -1;
int main() {
    
    
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
    for (int i = 1; i <= n; i++) {
    
    
        _min = 1e9 + 5;
        for (int j = 1; j <= n; j++) {
    
    
            _min = min(_min, abs(a[i] - b[j]));
        }
        _max = max(_max, _min);
    }
    printf("%d", _max);
}

猜你喜欢

转载自blog.csdn.net/cqbzlydd/article/details/104174499