当前位置: 首页 > news >正文

nim游戏及其进阶 [SDOI2011] 黑白棋 [SDOI2019] 移动金币

最近在做博弈题单,遇到了这两个nim问题。

[SDOI2019] 移动金币 被成为阶梯nim,也就是一个 n n n堆石子在不同的阶梯上面,每次操作可以选择一堆中若干个石子合并到低一级的阶梯(如果移动到地面就不能再移动),不能操作者失败。

这个问题我大概思考了一天就解决了,容易发现n=2时必败态为第一级阶梯为0,因为这样先手移动任何石子下来后手都能移到地面。把n拓展到更大,就会发现偶数级阶梯(现在假设地面为第0级阶梯)的石子时没有意义的,因为如果把偶数的石子移到奇数的石子能形成更好的局面,那么后手就能把这些石子再往移动一级,可以依次从第二级,第四级往下递推思考。

也就是说只用考虑奇数级阶梯的石子,又因为把石子移到偶数级的阶梯以后没有意义,那么就变成了在偶数级阶梯进行经典的nim操作,因此必败态就是所有偶数级阶梯的异或和。

[SDOI2019] 移动金币 被称为k-nim也就是每次可以取不超过k堆的石子。我花了大概一周才解决,感觉非常让人抓狂,最后是在操场上散步突然想到结论的。这个结论现在还没有想好怎么证明,后续补上。

感觉这个问题打表比较困难,不过我想了一种直接打出必败态的方法。

#include <bits/stdc++.h>
#define ll long long
using namespace std;

const int V = 8, N = 1e6 + 10;

int n, m;
int a[15];
int cnt, b[N][15], f[N], g[N];

int calc(int p1, int p2) {
    int ret = 0;
    for (int i = 1, j = 1; i <= n; i++) {
        while (j <= n && b[p1][i] > b[p2][j]) j++;
        if (j <= n && b[p1][i] == b[p2][j]) {
            ret++; j++;
        }
    }
    return n - ret;
}

void dfs(int k, int last) {
    if (k == n + 1) {
        cnt++;
        g[cnt] = 0;
        for (int i = 1; i <= n; i++) {
            b[cnt][i] = a[i];
            g[cnt] ^= a[i];
        }
        f[cnt] = 2;
        for (int i = 1; i < cnt; i++) {
            if (f[i] == 1) continue;
            bool ok = 1;
            for (int j = 1; j <= n; j++) {
                if (b[i][j] > b[cnt][j]) {
                    ok = 0; break;
                }
            }
            if (ok && calc(i, cnt) <= m) {
                f[cnt] = 1; break;
            }
        }
        if (f[cnt] == 1) {
            cnt--;
        }
        return;
    }
    for (int i = last; i <= V; i++) {
        a[k] = i;
        dfs(k + 1, i);
    }
}

int main() {
    // freopen("out.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m;
    dfs(1, 0);
    for (int i = 1; i <= cnt; i++) {
        bool bk = 1;
        for (int ki = 10; ki >= 0; ki--) {
            int ret = 0;
            for (int j = 1; j <= n; j++) {
                if (b[i][j] >> ki & 1) {
                    ret++;
                }
            }
            if (ret % (m + 1) != 0) {
                cout << "wa\n";
            }
        }
        if (f[i] == 2) {
            cout << f[i] << " " << g[i] << " : ";
            for (int j = 1; j <= n; j++) {
                cout << b[i][j] << " \n"[j == n];
            }
        }
    }
    cout << cnt << endl;
}

很早的时候我就想到了如果每一堆都是1,那么必败态就是n%(d+1)=0,说明结论还与n和d有关系(虽然应该和n没有关系,不过我当时就觉得它们有关系)。

然后想到一个比较奇怪的事情,以前学nim的时候我就一直十分不解为什么结论会和xor有关系(因为异或是一个二进制的操作),因为异或实际上是把每位相加再模二,然后就感觉可能会是模数变化(变成d+1)这样操作了肯定就不为全为0了,或者是进制变化(比如变成d+1进制之类的)。

而且前面打表的时候也发现必败态里面往往没有8的出现,所以更加肯定是模数改变。

验证了一下发现是正确的,然后就ac了。

相关文章:

  • Android系统开发 给system/app传包报错
  • 【CUDA 】第4章 全局内存——4.4 核函数可达到的带宽(4对角转置)
  • CentOS建立ssh免密连接(含流程剖析)
  • IPoIB 接收数据流程分析
  • 以教代学——费曼学习法
  • 计算机毕业设计Python房价预测 房源推荐系统 房源分析可视化(源码+LW文档+PPT+详细讲解)
  • HTML/CSS中并集选择器
  • Huatuo热更新--如何使用
  • 解析DrugBank数据库数据|Python
  • 每日一题——37.解数独
  • 23. AI-大语言模型-DeepSeek赋能开发-Spring AI集成
  • 初步安装和使用vant组件库,使用css变量定制vant主题样式 ,小程序的API Promise化,调用promise化之API
  • Lineageos 22.1(Android 15) 开机向导制作
  • 【0407】Postgres内核 Condition variables (ConditionVariable)设计机制 ①
  • HDLBits ——> Building Larger Circuits
  • Windows桌面系统管理5:Windows 10操作系统注册表
  • ubuntu源码方式安装TensorRT-LLM推理框架(超详细)
  • 亲测可用,IDEA中使用满血版DeepSeek R1!支持深度思考!免费!免配置!
  • idea连接gitee(使用idea远程兼容gitee)
  • Redis7——基础篇(五)
  • 沙县小吃中东首店在沙特首都利雅得开业,首天营业额超5万元
  • 扶桑谈|从石破茂“越菲行”看日本周边外交布局战略新动向
  • 上海建筑领域绿色发展2025年工作要点发布
  • 这些网红果蔬正在收割你的钱包,营养师:吃了个寂寞
  • 浙江公开征集涉企行政执法问题线索,包括乱收费、乱罚款等
  • 75万采购防火墙实为299元路由器?重庆三峡学院发布终止公告:出现违法违规行为