对于一些博弈问题的总结

可能是自己做的题目不够多把,自己目前对博弈题的认识是:只要是符合nim规则的博弈,都可以用sg函数求解,但是如果我们只考虑单个游戏,不考虑游戏之间的组合,只考虑必败态和必胜态就好了,自己能力有限,目前对这个结论只有一个感性上的认识,因为自己对sg函数的认识只停留在mex操作这个低级的层面,虽然维基百科上有sg相关的证明,但是自己英语的阅读能力太差,又忙于训练,没有太多的时间。希望大家原谅我不能严谨地证明这个结论,也希望大家能对我提出各种意见。

组合游戏

组合游戏必须使用sg函数,因为各个独立的游戏之间的必败态和必胜态之间已经失去了关联性,无法单纯地用P,N这两个简单的符号表示,而是要利用sg函数以及各个独立游戏sg值的异或和来深层的挖掘目前状态的性质。网上有很多sg函数相关的入门文章,但我觉得这些文章大多都是点到即止,如果你想深入地了解sg函数的相关原理,应该选择直接阅读相关论文,如果你只是想可以成功地切几个题,恕我愚昧的言论:只要记结论就够了。(我不是不鼓励大家去了解背后的原理,我只是想说,在我个人的理解中,如果精力有限,sg函数是无法很好理解的,浅尝辄止的话,反而更不好)

在一些大数据的题目里,sg函数的计算会直接导致TLE,这时就要通过本地打一些小数据的表,来试图找到题目里的规律,当然,有些题目我们可以通过自己分析来得到解题需要的规律或者结论,是通过分析还是通过打表得出,就是仁者见仁智者见智了。

丢一道昨天做到的利用sg函数的博弈题(只是昨天做到而已,没有代表性,但依然可以做做熟悉下sg函数):http://acm.tju.edu.cn/toj/showp4172.html
自己的代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define MS(x, y) memset(x, y, sizeof(x))

const int MAXN = 1e3 + 5;

int n;
int sg[MAXN];
bool sgVis[MAXN];
char str[MAXN];

int main() {
  for (int i = 2; i < MAXN; ++i) {
    for (int j = 0; j <= i - 2; ++j) {
      sgVis[sg[j] ^ sg[i - 2 - j]] = true;
    }
    for (int j = 0; j < MAXN; ++j) if (!sgVis[j]) {
      sg[i] = j;
      break;
    }
    for (int j = 0; j <= i - 2; ++j) {
      sgVis[sg[j] ^ sg[i - 2 - j]] = false;
    }
//    cout << i << ": " << sg[i] << endl;
  }
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%s", &n, str);
    int cnt = 0, ans = 0;
    for (int i = 0; str[i]; ++i) {
      if (str[i] == '0') {
        ans ^= sg[cnt];
        cnt = 0;
      } else ++cnt;
    }
    ans ^= sg[cnt];
    puts(ans ? "Alice" : "Bob");
  }
}

非组合游戏

其实就是只有一个游戏的意思了,这种题我们可以不依靠sg函数,直接通过必败点和必胜点的判断来找到解题的方法。这样少了mex操作,可以节省时间,自己思考的时候因为只有P和N两种状态,思考起来比sg值更直观。

丢一道最近做过的题:http://acmoj.shu.edu.cn/problem/418/,这题直接通过N点P点打表就好了,即:必败点的前驱一定是必胜点,不需要考虑sg函数。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int MAXN = 500 + 5;

int n, m;

bool notprime[MAXN];
int prime[MAXN], pnum;
bool win[MAXN][MAXN];

int main() {
  for (int i = 2; i < MAXN; ++i) if (!notprime[i]) {
    prime[pnum++] = i;
    for (int j = i * 2; j < MAXN; j += i) notprime[j] = true;
  }
  for (int i = 1; i < MAXN; ++i) for (int j = 1; j < MAXN; ++j) if (!win[i][j]) {
    for (int k = 0; k < pnum; ++k) {
      if (i + prime[k] < MAXN) win[i + prime[k]][j] = true;
      if (j + prime[k] < MAXN) win[i][j + prime[k]] = true;
      if (i + prime[k] < MAXN && j + prime[k] < MAXN) win[i + prime[k]][j + prime[k]] = true;
    }
  }
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%d", &n, &m);
    if (win[n][m]) puts("Sora");
    else puts("Shiro");
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值