还得是大强子,这次村里是真发钱了

教育   2025-01-07 14:36   广东  

刘强东

今天,一封《致光明村乡亲们的拜年信》刷屏了。

信中的内容除了常规的春节祝福以外,还提到了会给村民们送来品种繁多的年货礼物,60 岁以上的长辈,还将额外得到 1 万元现金,而当年在校的老师们,则每人可得 10 万元。

时间是 2025.1.7,落款人是刘强东。

其实关于「刘强东要给村里 60 岁以上老人发钱」的事儿,早在前几周就开始发酵,只不过颇具仪式感的通知,则是今天才来到。

刘强东的故事,相信大家都不陌生。

当年他上大学时(28 年前),全村人为他凑了 500 元和 76 个鸡蛋作为学费和生活费,这个行为对刘强东来说意义重大,他也曾多次在公开场合提到此事,因此刘强东任何时刻以任何形式对乡亲们表达感激之情,我都不会感到意外。

只不过,如果能够勿忘初心,对自己底层一线员工(尤其是快递员)再好点就更好了。

对此,你怎么看?此时此刻是不是想成为东哥(物理上)的亲人?

...

回归主题。

来一道和「腾讯」相关的算法题。

题目描述

平台:LeetCode

题号:464

在 "100 game" 这个游戏中,两名玩家轮流选择从 的任意整数,累计整数和,先使得累计整数和达到或超过  的玩家,即为胜者。

如果我们将游戏规则改为 “玩家不能重复使用整数” 呢?

例如,两个玩家可以轮流从公共整数池中抽取从 的整数(不放回),直到累计整数和 >=

给定两个整数 maxChoosableInteger (整数池中可选择的最大数)和 desiredTotal(累计和),若先出手的玩家是否能稳赢则返回 true ,否则返回 false 。假设两位玩家游戏时都表现 最佳 。

示例 1:

输入:maxChoosableInteger = 10, desiredTotal = 11

输出:false

解释:
无论第一个玩家选择哪个整数,他都会失败。
第一个玩家可以选择从 1 到 10 的整数。
如果第一个玩家选择 1,那么第二个玩家只能选择从 2 到 10 的整数。
第二个玩家可以通过选择整数 10(那么累积和为 11 >= desiredTotal),从而取得胜利.
同样地,第一个玩家选择任意其他整数,第二个玩家都会赢。

示例 2:

输入:maxChoosableInteger = 10, desiredTotal = 0

输出:true

示例 3:

输入:maxChoosableInteger = 10, desiredTotal = 1

输出:true

提示:

二维博弈论 DP(TLE)

这是一道博弈论 DP 的题,为了方便,我们使用 来表示 ,使用 来表示

由于 数据范围为 ,且每个数只能选一次,我们可以使用一个二进制数 来表示 范围内的被选择的数的情况:二进制表示中 的位置代表数已被选择,否则代表尚未选择。

首先朴素二维状态表示相对容易想到:定义 为当前已被选择的数为 ,轮数为 时,「原始回合的先手」能否获胜( 代表能, 代表不能),其中 开始,通过 的奇偶性可知是原始回合的先手还是后手。

设计递归函数来实现「记忆化搜索」,函数 int dfs(int state, int tot, int k) 表示当前状态为 对应累计和, 代表轮数,最终答案通过判断 dfs(0, 0, 0) 是否为 来得知。

转移过程中,如果发现当前回合的决策,能够直接使得累积和超过 ,说明当前回合玩家获胜;或者如果当前决策能够导致下一回合的玩家失败的话,当前回合玩家也获胜,否则当前玩家失败。

代码:

class Solution {
    int n, t;
    int[][] f = new int[1 << 20][2];
    // 1 true / -1 false
    int dfs(int state, int tot, int k) {
        if (state == ((1 << n) - 1) && tot < t) return -1;
        if (f[state][k % 2] != 0return f[state][k % 2];
        int hope = k % 2 == 0 ? 1 : -1;
        for (int i = 0; i < n; i++) {
            if (((state >> i) & 1) == 1continue;
            if (tot + i + 1 >= t) return f[state][k % 2] = hope;
            if (dfs(state | (1 << i), tot + i + 1, k + 1) == hope) return f[state][k % 2] = hope;
        }
        return f[state][k % 2] = -hope;
    }
    public boolean canIWin(int _n, int _t) {
        n = _n; t = _t;
        if (t == 0return true;
        return dfs(000) == 1;
    }
}

C++ 代码:

class Solution {
public:
    int n, t;
    vector<vector<int>> f;
    int dfs(int state, int tot, int k) {
        if (state == (1 << n) - 1 && tot < t) return -1;
        if (f[state][k % 2] != 0return f[state][k % 2];
        int hope = k % 2 == 0 ? 1 : -1;
        for (int i = 0; i < n; i++) {
            if ((state >> i) & 1continue;
            if (tot + i + 1 >= t) return f[state][k % 2] = hope;
            if (dfs(state | (1 << i), tot + i + 1, k + 1) == hope) return f[state][k % 2] = hope;
        }
        return f[state][k % 2] = -hope;
    }
    bool canIWin(int _n, int _t) {
        n = _n; t = _t;
        if (t == 0return true;
        f.resize(1 << 20vector<int>(20));
        return dfs(000) == 1;
    }
};
  • 时间复杂度:共有 个状态,每个状态转移需要 复杂度,整体复杂度为
  • 空间复杂度:

优化状态表示

进一步发现,若能优化轮数维度,可以有效减少一半的计算量,我们调整状态定义为:定义 为当前状态为 ,「当前先手」能否获胜( 代表能, 代表不能)。

同时调整递归函数为 ,最终答案通过判断 dfs(0, 0) 是否为 来得知。

注意这里调整的重点在于:将记录「原始回合的先后手发起 和 原始回合的先后手获胜情况」调整为「当前回合发起 和 当前回合获胜情况」。

Java 代码:

class Solution {
    int n, t;
    int[] f = new int[1 << 20];
    // 1 true / -1 false
    int dfs(int state, int tot) {
        if (f[state] != 0return f[state];
        for (int i = 0; i < n; i++) {
            if (((state >> i) & 1) == 1continue;
            if (tot + i + 1 >= t) return f[state] = 1;
            if (dfs(state | (1 << i), tot + i + 1) == -1return f[state] = 1;
        }
        return f[state] = -1;
    }
    public boolean canIWin(int _n, int _t) {
        n = _n; t = _t;
        if (n * (n + 1) / 2 < t) return false;
        if (t == 0return true;
        return dfs(00) == 1;
    }
}

C++ 代码:

class Solution {
public:
    int n, t;
    vector<int> f;
    int dfs(int state, int tot) {
        if (f[state] != 0return f[state];
        for (int i = 0; i < n; i++) {
            if ((state >> i) & 1continue;
            if (tot + i + 1 >= t) return f[state] = 1;
            if (dfs(state | (1 << i), tot + i + 1) == -1return f[state] = 1;
        }
        return f[state] = -1;
    }
    bool canIWin(int _n, int _t) {
        n = _n; t = _t;
        if (n * (n + 1) / 2 < t) return false;
        if (t == 0return true;
        f.resize(1 << 200);
        return dfs(00) == 1;
    }
};

Python 代码:

class Solution:
    def __init__(self):
        self.n = 0
        self.t = 0
        self.f = [0] * (1 << 20)

    def dfs(self, state, tot):
        if self.f[state] != 0:
            return self.f[state]
        for i in range(self.n):
            if (state >> i) & 1:
                continue
            if tot + i + 1 >= self.t:
                self.f[state] = 1
                return 1
            if self.dfs(state | (1 << i), tot + i + 1) == -1:
                self.f[state] = 1
                return 1
        self.f[state] = -1
        return -1

    def canIWin(self, _n, _t):
        self.n = _n
        self.t = _t
        if self.n * (self.n + 1) // 2 < self.t:
            return False
        if self.t == 0:
            return True
        return self.dfs(00) == 1
  • 时间复杂度:共有 个状态,每个状态转移需要 复杂度,整体复杂度为
  • 空间复杂度:

最后

巨划算的 LeetCode 会员优惠通道目前仍可用 ~

使用福利优惠通道 leetcode.cn/premium/?promoChannel=acoier,年度会员 有效期额外增加两个月,季度会员 有效期额外增加两周,更有超大额专属 🧧 和实物 🎁 福利每月发放。

我是宫水三叶,每天都会分享算法知识,并和大家聊聊近期的所见所闻

欢迎关注,明天见。



宫水三叶的刷题日记
锐评时事热点的 算法与数据结构 题解区博主。「 刷穿 LeetCode 」系列文章原创公众号。
 最新文章