【题解】—— LeetCode一周小结5


【题解】—— 每日一道题目栏


上接:【题解】—— LeetCode一周小结4

29.自由之路

题目链接:514. 自由之路

电子游戏“辐射4”中,任务 “通向自由” 要求玩家到达名为 “Freedom Trail Ring” 的金属表盘,并使用表盘拼写特定关键词才能开门。

给定一个字符串 ring ,表示刻在外环上的编码;给定另一个字符串 key ,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。

最初,ring 的第一个字符与 12:00 方向对齐。您需要顺时针或逆时针旋转 ring 以使 key 的一个字符在 12:00 方向对齐,然后按下中心按钮,以此逐个拼写完 key 中的所有字符。

旋转 ring 拼出 key 字符 key[i] 的阶段中:

  1. 您可以将 ring 顺时针或逆时针旋转 一个位置 ,计为1步。旋转的最终目的是将字符串 ring 的一个字符与 12:00 方向对齐,并且这个字符必须等于字符 key[i] 。
  2. 如果字符 key[i] 已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。

示例 1:

请添加图片描述

解释:

对于 key 的第一个字符 ‘g’,已经在正确的位置, 我们只需要1步来拼写这个字符。
对于 key 的第二个字符’d’,我们需要逆时针旋转 ring “godding” 2步使它变成 “ddinggo”。
当然, 我们还需要1步进行拼写。
因此最终的输出是 4。

示例 2:

输入: ring = “godding”, key = “godding”

输出: 13

提示:

1 <= ring.length, key.length <= 100

ring 和 key 只包含小写英文字母

保证 字符串 key 一定可以由字符串 ring 旋转拼出

题解
方法:动态规划
        我们用 dp[i][j] 表示从游戏开始到拼接完成 key[0:i] 在 ring[j]==key[i] 的时候最小步数,所以 dp 的最大长度是 dp[key.length][ring.length]

        什么是:在 ring[j]==key[i] 的时候最小步数?请添加图片描述
这是本题的难点所在,因为存在重复字符串,所以我们把所有重复的位置都考虑到。

        比如一下情况:

        ring=“ababac”
        key=“ab”

        第一步 我们先开始拼接 a,我们要把 ring 中每一个a都计算到

        计算的结果是:

        dp[0][0]=1
        dp[0][2]=3
        dp[0][4]=4

        为什么出现上面三个结果,因为 ring 中总共有三个 a,比如 dp[0][4] 的第二维度 4 表示 a 在 ring 中的下标。

        第二步 我们开始算 b,上一步指针停留的位置必然是某个 a 的位置,我们无法知道哪个 a 的位置更优,所以都考虑到,也就是说我们对于上个每一个 a 得位置和现在每一个 b 得位置 都要计算。

        比如对于在位置 1 的 b,我们要算 dp[1][1]=min(dp[0][0]+m+1,dp[0][2]+m+1,dp[0][4]+m+1)

        其中 m 是两个位置的距离

        比如对于在位置 2 的 b,我们要算 dp[1][3]=min(dp[0][0]+m+1,dp[0][2]+m+1,dp[0][4]+m+1)

        下图是上述的所以搜索路径

        最后答案是所有的 dp[1][index(a)] 的最小值。
        我们可以开始用和哈希表来保存每一个字符在 ring 中所有出现位置,来节约时间

class Solution {
    Map<Character,List<Integer>> mp;
    int[][] dp;
    public int findRotateSteps(String ring, String key) {
        int rl=ring.length();
        int kl=key.length();
        if(kl==0) return 0;
        char[] r=ring.toCharArray();
        char[] k=key.toCharArray();
        mp = new HashMap<>();
        for(int i=0;i<rl;i++){//保存字符出现的位置
            if(mp.containsKey(r[i])){
                mp.get(r[i]).add(i);
            }
            else{
                List<Integer> next= new ArrayList<>();
                next.add(i);
                mp.put(r[i],next);
            }
        }
        dp = new int[kl][rl];
        List<Integer> next2 = mp.get(k[0]);
        Iterator<Integer> it2 = next2.iterator();
        while(it2.hasNext()){
            int c = it2.next();
            int m = Math.min(c,rl-c);//找到每个位置
            dp[0][c]=m+1;
        }
        for(int i=1;i<kl;i++){

            List<Integer> next = mp.get(k[i]);
            Iterator<Integer> it = next.iterator();
            while(it.hasNext()){
                int c = it.next();//找到本次的所有位置
                int min=Integer.MAX_VALUE;
                List<Integer> next1 = mp.get(k[i-1]);
                Iterator<Integer> it1 = next1.iterator();
                while(it1.hasNext()){
                    int d = it1.next();//找到上个字符所有的位置来计算
                    int m = Math.min(rl-c+d,rl-d+c);
                    m = Math.min(m,Math.abs(c-d));
                    min=Math.min(min,dp[i-1][d]+m+1);
                    
                }
                dp[i][c]=min;
            }
        }
        int ans=Integer.MAX_VALUE;
        List<Integer> next = mp.get(k[kl-1]);
        Iterator<Integer> it = next.iterator();
        while(it.hasNext()){
            ans=Math.min(ans,dp[kl-1][it.next()]);
        }
        return ans;
        
        
    }
    
}

30.使循环数组所有元素相等的最少秒数

题目链接:2808. 使循环数组所有元素相等的最少秒数

给你一个下标从 0 开始长度为 n 的数组 nums 。

每一秒,你可以对数组执行以下操作:

对于范围在 [0, n - 1] 内的每一个下标 i ,将 nums[i] 替换成 nums[i] ,nums[(i - 1 + n) % n] 或者 nums[(i + 1) % n] 三者之一。
注意,所有元素会被同时替换。

请你返回将数组 nums 中所有元素变成相等元素所需要的 最少 秒数。

示例 1:

输入:nums = [1,2,1,2]

输出:1

解释:我们可以在 1 秒内将数组变成相等元素:

  • 第 1 秒,将每个位置的元素分别变为 [nums[3],nums[1],nums[3],nums[3]] 。变化后,nums = [2,2,2,2] 。

1 秒是将数组变成相等元素所需要的最少秒数。

示例 2:

输入:nums = [2,1,3,3,2]

输出:2

解释:我们可以在 2 秒内将数组变成相等元素:

  • 第 1 秒,将每个位置的元素分别变为 [nums[0],nums[2],nums[2],nums[2],nums[3]] 。变化后,nums = [2,3,3,3,3] 。
  • 第 2 秒,将每个位置的元素分别变为 [nums[1],nums[1],nums[2],nums[3],nums[4]] 。变化后,nums = [3,3,3,3,3] 。

2 秒是将数组变成相等元素所需要的最少秒数。

示例 3:

输入:nums = [5,5,5,5]

输出:0

解释:不需要执行任何操作,因为一开始数组中的元素已经全部相等。

提示:

1 <= n == nums.length <= 105

1 <= nums[i] <= 109

题解
方法:把问题看成是【扩散】元素
        
提示 1

最终所有元素一定变成了一个在 nums 中的数。

枚举这个数。

提示 2

考虑把数字 x 扩散到其它位置,那么每一秒 x 都可以向左右扩散一位。

多个相同数字 x 同时扩散,那么扩散完整个数组的耗时,就取决于相距最远的两个相邻的 x。

假设这两个 x 的下标分别为 i 和 j,且 i<j,那么耗时为:

⌊(j−i)/2⌋不同的 x,计算相应的耗时,更新答案的最小值。

提示 3

统计所有相同数字的下标,记到一个哈希表 pos 中。

本题数组可以视作是环形的,假设最左边的 x 的下标是 i,只需要在 pos[x] 列表末尾添加一个 i+n,就可以转换成非环形数组处理了。

public class Solution {
    public int minimumSeconds(List<Integer> nums) {
        int n = nums.size();
        Map<Integer, List<Integer>> pos = new HashMap<>();
        for (int i = 0; i < n; i++) {
            pos.computeIfAbsent(nums.get(i), k -> new ArrayList<>()).add(i);
        }

        int ans = n;
        for (List<Integer> a : pos.values()) {
            a.add(a.get(0) + n);
            int mx = 0;
            for (int i = 1; i < a.size(); ++i) {
                mx = Math.max(mx, (a.get(i) - a.get(i - 1)) / 2);
            }
            ans = Math.min(ans, mx);
        }
        return ans;
    }
}

31.找出不同元素数目差数组

题目链接:2670. 找出不同元素数目差数组

给你一个下标从 0 开始的数组 nums ,数组长度为 n 。

nums 的 不同元素数目差 数组可以用一个长度为 n 的数组 diff 表示,其中 diff[i] 等于前缀 nums[0, …, i] 中不同元素的数目 减去 后缀 nums[i + 1, …, n - 1] 中不同元素的数目。

返回 nums 的 不同元素数目差 数组。

注意 nums[i, …, j] 表示 nums 的一个从下标 i 开始到下标 j 结束的子数组(包含下标 i 和 j 对应元素)。特别需要说明的是,如果 i > j ,则 nums[i, …, j] 表示一个空子数组。

示例 1:

输入:nums = [1,2,3,4,5]

输出:[-3,-1,1,3,5]

解释:

对于 i = 0,前缀中有 1 个不同的元素,而在后缀中有 4 个不同的元素。因此,diff[0] = 1 - 4 = -3 。

对于 i = 1,前缀中有 2 个不同的元素,而在后缀中有 3 个不同的元素。因此,diff[1] = 2 - 3 = -1 。

对于 i = 2,前缀中有 3 个不同的元素,而在后缀中有 2 个不同的元素。因此,diff[2] = 3 - 2 = 1 。

对于 i = 3,前缀中有 4 个不同的元素,而在后缀中有 1 个不同的元素。因此,diff[3] = 4 - 1 = 3 。

对于 i = 4,前缀中有 5 个不同的元素,而在后缀中有 0 个不同的元素。因此,diff[4] = 5 - 0 = 5 。

示例 2:

输入:nums = [3,2,3,4,2]

输出:[-2,-1,0,2,3]

解释:

对于 i = 0,前缀中有 1 个不同的元素,而在后缀中有 3 个不同的元素。因此,diff[0] = 1 - 3 = -2 。

对于 i = 1,前缀中有 2 个不同的元素,而在后缀中有 3 个不同的元素。因此,diff[1] = 2 - 3 = -1 。

对于 i = 2,前缀中有 2 个不同的元素,而在后缀中有 2 个不同的元素。因此,diff[2] = 2 - 2 = 0 。

对于 i = 3,前缀中有 3 个不同的元素,而在后缀中有 1 个不同的元素。因此,diff[3] = 3 - 1 = 2 。

对于 i = 4,前缀中有 3 个不同的元素,而在后缀中有 0 个不同的元素。因此,diff[4] = 3 - 0 = 3 。

提示:

1 <= n == nums.length <= 50

1 <= nums[i] <= 50

题解
方法:O(n) 前后缀分解
        先倒序枚举 nums,并记录每个后缀的不同元素个数到 suf 数组中,这可以用哈希表做到。

然后正序枚举 nums[i],同样地,记录每个前缀的不同元素个数,减去 suf[i+1],即为答案。

class Solution {
    public int[] distinctDifferenceArray(int[] nums) {
        int n = nums.length;
        var suf = new int[n + 1];
        var s = new HashSet<Integer>();
        for (int i = n - 1; i > 0; i--) {
            s.add(nums[i]);
            suf[i] = s.size();
        }

        s.clear();
        var ans = new int[n];
        for (int i = 0; i < n; i++) {
            s.add(nums[i]);
            ans[i] = s.size() - suf[i + 1];
        }
        return ans;
    }
}

2024.2

1.数字游戏

题目链接:LCP 24. 数字游戏

小扣在秋日市集入口处发现了一个数字游戏。主办方共有 N 个计数器,计数器编号为 0 ~ N-1。每个计数器上分别显示了一个数字,小扣按计数器编号升序将所显示的数字记于数组 nums。每个计数器上有两个按钮,分别可以实现将显示数字加一或减一。小扣每一次操作可以选择一个计数器,按下加一或减一按钮。

主办方请小扣回答出一个长度为 N 的数组,第 i 个元素(0 <= i < N)表示将 0~i 号计数器 初始 所示数字操作成满足所有条件 nums[a]+1 == nums[a+1],(0 <= a < i) 的最小操作数。回答正确方可进入秋日市集。

由于答案可能很大,请将每个最小操作数对 1,000,000,007 取余。

示例 1:

输入:nums = [3,4,5,1,6,7]

输出:[0,0,0,5,6,7]

解释: i = 0,[3] 无需操作 i = 1,[3,4] 无需操作; i = 2,[3,4,5] 无需操作; i = 3,将
[3,4,5,1] 操作成 [3,4,5,6], 最少 5 次操作; i = 4,将 [3,4,5,1,6] 操作成
[3,4,5,6,7], 最少 6 次操作; i = 5,将 [3,4,5,1,6,7] 操作成 [3,4,5,6,7,8],最少 7
次操作; 返回 [0,0,0,5,6,7]。

示例 2:

输入:nums = [1,2,3,4,5]

输出:[0,0,0,0,0]

解释:对于任意计数器编号 i 都无需操作。

示例 3:

输入:nums = [1,1,1,2,3,4]

输出:[0,1,2,3,3,3]

解释:
i = 0,无需操作; i = 1,将 [1,1] 操作成 [1,2] 或 [0,1] 最少 1 次操作; i = 2,将
[1,1,1] 操作成 [1,2,3] 或 [0,1,2],最少 2 次操作; i = 3,将 [1,1,1,2] 操作成
[1,2,3,4] 或 [0,1,2,3],最少 3 次操作; i = 4,将 [1,1,1,2,3] 操作成
[-1,0,1,2,3],最少 3 次操作; i = 5,将 [1,1,1,2,3,4] 操作成 [-1,0,1,2,3,4],最少 3
次操作; 返回 [0,1,2,3,3,3]。

提示:

1 <= nums.length <= 105

1 <= nums[i] <= 103

题解
方法:求数据流的中位数
        下文中所有 “数组 nums 的前 i 个元素” 均指 nums[0] 到 nums[i] 的 i + 1 个元素

        题目中要求满足 nums[a] + 1 == nums[a + 1] , 等价于满足 nums[a] - a == nums[a + 1] - (a + 1), 所以我们可以先对数组进行预处理, 将每个nums[i] 替换为 nums[i] - i, 问题就变成寻找最小的操作次数使得数组的前 i 个元素相等, 假设操作后得到的这个相等的元素为 median[i], 即:

median[i]={argmin_x} ∑ j − 0 i ∣ n u m s [ j ] − x ∣ \sum\limits_{j-0}^i\lvert nums[j]−x\rvert j0inums[j]x

        那么当前有奇数个元素时, median[i] 必然是数组前 i 个元素的中位数, 有偶数个元素时 median[i] 则可以是排序后最中间两个数构成的区间内的任意一个值 (不理解这一点的可以画一个数轴感受一下), 所以我们可以利用 295. 数据流的中位数 中维护两个堆的方法快速求出 nums 数组前 i 个元素的中位数.

        求得中位数后, 如果直接遍历 [0, i] 求需要的操作次数会超时, 所以我们将求中位数的算法稍加修改, 维护数据流较小的一半的和 maxSum (即小于中位数的所有数之和) 与较大的一半的和 minSum (即大于等于中位数的所有数之和), 即:

minSum = ∑ j = 0 ⋯ i , n u m s [ j ] ≥ m e d i a n [ i ]   n u m s [ j ] \sum\limits_{j=0⋯i,nums[j]≥median[i]}\ {nums[j]} j=0i,nums[j]median[i] nums[j]

maxSum= ∑ j = 0 ⋯ i , n u m s [ j ] < m e d i a n [ i ]   n u m s [ j ] \sum\limits_{j=0⋯i,nums[j]<median[i]}\ {nums[j]} j=0i,nums[j]<median[i] nums[j]

所以:

∑ j − 0 i ∣ n u m s [ j ] − m e d i a n [ i ] ∣ \sum\limits_{j-0}^i\lvert nums[j]−median[i]\rvert j0inums[j]median[i]∣ = minSum − card({j∣nums[j]≥median[i],0≤j≤i})×median[i]+(card({j∣nums[j]<median[i],0≤j≤i})×median[i]−maxSum)

这样我们就不用数据流中每加入一个元素后重新求和, 从而以 O(nlogn) 复杂度解决问题.

注意代码中 minHeap 代表的小顶堆维护的是数据流较大的一半, 而 maxHeap 则是较小的一半.

class Solution {
    public int[] numsGame(int[] nums) {
        int n = nums.length;
        int[] result = new int[n];
        for (int i = 0; i < n; i++)
            nums[i] -= i;
        result[0] = 0;
        MedianFinder finder = new MedianFinder();
        finder.addNum(nums[0]);
        for (int i = 1; i < n; i++) {
            finder.addNum(nums[i]);
            int median = finder.findMedian();
            long value = finder.minSum - (long) (finder.minHeap.size() - 1) * (long) median
                    + ((long) (finder.maxHeap.size() - 1) * (long) median - finder.maxSum);
            result[i] = (int) (value % 1000000007);
        }
        return result;
    }
}

class MedianFinder {
    PriorityQueue<Integer> minHeap, maxHeap;
    long minSum = 0, maxSum = 0;

    public MedianFinder() {
        minHeap = new PriorityQueue<>();
        maxHeap = new PriorityQueue<>(1, (x, y) -> {
            long result = (long) y - (long) x;
            if (result > 0)
                return 1;
            if (result < 0)
                return -1;
            return 0;
        });
        minHeap.add(Integer.MAX_VALUE);
        maxHeap.add(Integer.MIN_VALUE);
    }

    private void adjust() {
        int maxSize = maxHeap.size(), minSize = minHeap.size();
        if (maxSize - minSize >= 2) {
            int num = maxHeap.poll();
            maxSum -= num;
            minHeap.add(num);
            minSum += num;
        } else if (minSize - maxSize >= 2) {
            int num = minHeap.poll();
            minSum -= num;
            maxHeap.add(num);
            maxSum += num;
        }
    }

    public void addNum(int num) {
        int lowerMax = maxHeap.peek();
        if (num <= lowerMax) {
            maxHeap.add(num);
            maxSum += num;
        } else {
            minHeap.add(num);
            minSum += num;
        }
        adjust();
    }

    public int findMedian() {
        if (maxHeap.size() > minHeap.size())
            return maxHeap.peek();
        else
            return minHeap.peek();
    }
}


2.石子游戏 VI

题目链接:1686. 石子游戏 VI

Alice 和 Bob 轮流玩一个游戏,Alice 先手。

一堆石子里总共有 n 个石子,轮到某个玩家时,他可以 移出 一个石子并得到这个石子的价值。Alice 和 Bob 对石子价值有 不一样的的评判标准 。双方都知道对方的评判标准。

给你两个长度为 n 的整数数组 aliceValues 和 bobValues 。aliceValues[i] 和 bobValues[i] 分别表示 Alice 和 Bob 认为第 i 个石子的价值。

所有石子都被取完后,得分较高的人为胜者。如果两个玩家得分相同,那么为平局。两位玩家都会采用 最优策略 进行游戏。

请你推断游戏的结果,用如下的方式表示:

  • 如果 Alice 赢,返回 1 。
  • 如果 Bob 赢,返回 -1 。
  • 如果游戏平局,返回 0 。

示例 1:

输入:aliceValues = [1,3], bobValues = [2,1]

输出:1

解释:

如果 Alice 拿石子 1 (下标从 0开始),那么 Alice 可以得到 3 分。Bob 只能选择石子 0 ,得到 2 分。Alice
获胜。

示例 2:

输入:aliceValues = [1,2], bobValues = [3,1]

输出:0

解释:

Alice 拿石子 0 , Bob 拿石子 1 ,他们得分都为 1 分。打平。

示例 3:

输入:aliceValues = [2,4,3], bobValues = [1,6,7]

输出:-1

解释:
不管 Alice 怎么操作,Bob 都可以得到比 Alice 更高的得分。 比方说,Alice 拿石子 1 ,Bob 拿石子 2,Alice 拿石子 0 ,Alice 会得到 6 分而 Bob 得分为 7 分。 Bob 会获胜。

提示:

n == aliceValues.length == bobValues.length

1 <= n <= 105

1 <= aliceValues[i], bobValues[i] <= 100

题解
方法:博弈
        在游戏进行的过程中,想要本轮自己的得分最优化,要同时考虑自己能够获得尽可能多得分,并且可以让对手损失尽可能多的得分。令 values[i]=aliceValues[i]+bobValues[i],values[x] 大,意味着自己可以获得对二者而言均比较大的石头,正好可以满足目标:自己的收益和让对方的损失权衡下来最大化。先对 values 逆序排序,两人轮流顺序取当前得分和对应的石头即可(对二者而言这么取都是最有利于自己的)。

class Solution {
    public int stoneGameVI(int[] aliceValues, int[] bobValues) {
        int n = aliceValues.length;
        List<int[]> value = new ArrayList<>();
        for (int i = 0; i < n; i++)
            value.add(new int[] {i, aliceValues[i] + bobValues[i]});
        value.sort((x, y) -> y[1] - x[1]);
        int total = 0;
        for (int i = 0; i < n; i++)
            total += i % 2 == 0 ? aliceValues[value.get(i)[0]] : -bobValues[value.get(i)[0]];
        return Integer.compare(total, 0);
    }
}

3.石子游戏 VII

题目链接:1690. 石子游戏 VII

石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 。

有 n 块石子排成一排。每个玩家的回合中,可以从行中 移除 最左边的石头或最右边的石头,并获得与该行中剩余石头值之 和 相等的得分。当没有石头可移除时,得分较高者获胜。

鲍勃发现他总是输掉游戏(可怜的鲍勃,他总是输),所以他决定尽力 减小得分的差值 。爱丽丝的目标是最大限度地 扩大得分的差值 。

给你一个整数数组 stones ,其中 stones[i] 表示 从左边开始 的第 i 个石头的值,如果爱丽丝和鲍勃都 发挥出最佳水平 ,请返回他们 得分的差值 。

示例 1:

输入:stones = [5,3,1,4,2]

输出:6

解释:

  • 爱丽丝移除 2 ,得分 5 + 3 + 1 + 4 = 13 。游戏情况:爱丽丝 = 13 ,鲍勃 = 0 ,石子 = [5,3,1,4] 。
  • 鲍勃移除 5 ,得分 3 + 1 + 4 = 8 。游戏情况:爱丽丝 = 13 ,鲍勃 = 8 ,石子 = [3,1,4] 。
  • 爱丽丝移除 3 ,得分 1 + 4 = 5 。游戏情况:爱丽丝 = 18 ,鲍勃 = 8 ,石子 = [1,4] 。
  • 鲍勃移除 1 ,得分 4 。游戏情况:爱丽丝 = 18 ,鲍勃 = 12 ,石子 = [4] 。
  • 爱丽丝移除 4 ,得分 0 。游戏情况:爱丽丝 = 18 ,鲍勃 = 12 ,石子 = [] 。

得分的差值 18 - 12 = 6 。

示例 2:

输入:stones = [7,90,5,1,100,10,10,2]

输出:122

提示:

n == stones.length

2 <= n <= 1000

1 <= stones[i] <= 1000

题解
方法:递归 记忆化搜索 动态规划 前缀和
        灵神教你一步步思考动态规划:从记忆化搜索到递推

方法:动态规划

public int stoneGameVII(int[] stones) {
    int n = stones.length;
    int[][] dp = new int[n][n];
    //通过动态方程,发现i和 >=i的有关系,j和<=j的有关系.
    //所以计算需要i从大到小,j从小到大,这样才能正确.
    for (int i = n - 2; i >= 0; i--) {
        for (int j = i + 1; j < n; j++) {
            if (i + 1 == j) {
                dp[i][j] = Math.max(stones[i], stones[j]);
            } else {
                //如果Alice先选i,那么bobo只能选i+1和j,这时候差就是stones[i+1]或者stones[j]
                //当然,还需要加上子区间dp的差值,才是最终的差值.
                //left和right就相当于是bobo选择后的差值,这个要尽可能小.所以是min
                //dp是Alice的差值,要尽可能大,所以是max
                int left = Math.min(stones[i + 1] + dp[i + 2][j], stones[j] + dp[i + 1][j - 1]);
                int right = Math.min(stones[i] + dp[i + 1][j - 1], stones[j - 1] + dp[i][j - 2]);
                dp[i][j] = Math.max(left, right);
            }
        }
    }
    return dp[0][n - 1];
}

4.Nim 游戏

题目链接:292. Nim 游戏

你和你的朋友,两个人一起玩 Nim 游戏:

  • 桌子上有一堆石头。
  • 你们轮流进行自己的回合, 你作为先手 。
  • 每一回合,轮到的人拿掉 1 - 3 块石头。
  • 拿掉最后一块石头的人就是获胜者。

假设你们每一步都是最优解。请编写一个函数,来判断你是否可以在给定石头数量为 n 的情况下赢得游戏。如果可以赢,返回 true;否则,返回 false 。

示例 1:

输入:n = 4

输出:false

解释:以下是可能的结果:

  1. 移除1颗石头。你的朋友移走了3块石头,包括最后一块。你的朋友赢了。
  2. 移除2个石子。你的朋友移走2块石头,包括最后一块。你的朋友赢了。
  3. 你移走3颗石子。你的朋友移走了最后一块石头。你的朋友赢了。

在所有结果中,你的朋友是赢家。

示例 2:

输入:n = 1

输出:true

示例 3:

输入:n = 2

输出:true

提示:

1 <= n <= 231 - 1

题解
方法:博弈论

class Solution {
    public boolean canWinNim(int n) {
        return n % 4 != 0;
    }
}

        巴什博弈:只有一堆n个物品,两个人轮流从这堆物品中取物, 规定每次至少取一个,最多取m个。最后取光者得胜。 只要 n 不能整除 m+1 ,那么必然是先手取胜,否则后手取胜


下接:【题解】—— LeetCode一周小结6


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/372200.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++:类的简单介绍(六)——初始化列表

目录 格式&#xff1a; 初始化之间的比较&#xff1a; 普通初始化的缺点&#xff1a; 初始化列表的优势&#xff1a; 必须进行初始化的变量 1、const 修饰的变量 2、被&修饰的变量 引用 3、自定义类型&#xff0c;且没有默认构造函数的成员变量必须走初始化列表…

QT6调用音频输入输出(超详细)

目录 一、QT6音频调用与QT5的区别 1.QAudioSource代替QAudioInput类 2.QAudioSink代替QAudioOutput类 二、音频操作中Push和Pull的区别 三、依托于Websocket实现实时对讲机 1.AudioIputDevices类 2.AudioOutputDevices类 3.实现的AudioHandler类完整内容 本人实际是要完…

作为一个27岁的人,学习单片机然后进入这行的可能性大吗?

作为一个27岁的人&#xff0c;学习单片机然后进入这行的可能性大吗&#xff1f;有c语言基础。&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「c语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“…

python-分享篇-小猪佩奇

文章目录 代码效果 代码 # coding:utf-8 import turtle as tt.pensize(4) t.hideturtle() t.colormode(255) t.color((255,155,192),"pink") t.setup(840,500) t.speed(10)#鼻子 t.pu() t.goto(-100,100) t.pd() t.seth(-30) t.begin_fill() a0.4 for i in range(12…

python爬虫概念及介绍

1. 什么是互联网爬虫&#xff1f; 解释 1 &#xff1a;通过一个程序&#xff0c;根据 Url ( http : // www . taobao . com ) 进行爬取网页&#xff0c;获取有用信息 解释 2&#xff1a;使用程序模拟浏览器&#xff0c;去向服务器发送请求&#xff0c;获取响应信息 2. 爬虫核…

C++病毒【永久性】

我最近发现&#xff0c;我2024年后就再也没有更新过 C#沙雕程序了。 今天我想通了&#xff0c;我要再更几期关于C#沙雕程序的文章。 开始做&#xff01; 这一次就直接上代码蚌&#xff01; 不用任何特定头文件。 #include <bits/stdc.h> #include <iostream> #…

【Java基础_02】Java变量

【Java基础_02】Java变量、运算符、程序控制结构 文章目录 1 变量1.1 程序中“”号的使用1.2 数据类型1.3 整数类型1.3.1 整数类型的分类1.3.2 整型的使用细节 1.4 浮点类型1.4.1 浮点型的分类1.4.2 浮点类型使用细节 1.5 字符类型1.5.1 字符类型使用细节1.5.2 字符类型本质1.5…

幻兽帕鲁专用服务器,多人游戏(专用服务器)搭建

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

网络安全产品之准入控制系统

文章目录 一、什么是准入控制系统二、准入控制系统的主要功能1. 接入设备的身份认证2. 接入设备的安全性检查 三、准入控制系统的工作原理四、准入控制系统的特点五、准入控制系统的部署方式1. 网关模式2. 控制旁路模式 六、准入控制系统的应用场景七、企业如何利用准入控制系统…

程序员为什么不喜欢关电脑,这回答很霸道!

在大家的生活中&#xff0c;经常会发现这样一个现象&#xff1a;程序员经常不关电脑。 至于程序员不关电脑的原因&#xff0c;众说纷纭。 其中这样的一个程序员&#xff0c;他的回答很霸道&#xff1a; “因为我是程序员&#xff0c;我有权选择不关电脑。我需要在任何时候都能够…

python创建udf函数步骤

一、目标 实现一个函数&#xff0c;传入两个datetime类型的参数&#xff0c;返回double类型的工作日天数 二、思路 如何计算差值&#xff1f; 如果开始时间和结束时间在同一天&#xff1a;实现同 datediff(end, start, ‘ss’) / 86400.0 如果开始时间和结束时间在不同天&am…

【教程】Python代码混淆工具,Python源代码保密、加密、混淆

引言 Python作为一种高级脚本语言&#xff0c;便捷的语法和丰富的库使它成为众多开发者的首选。然而&#xff0c;有时候我们希望保护我们的Python源代码&#xff0c;避免被他人轻易获取和篡改。为了实现这一目标&#xff0c;我们可以采取代码混淆的技术手段。本文将介绍Python…

vue3项目打包移除console.log()打印

一、安装terser&#xff08;不安装打包会报错&#xff0c;安装过后无需引入直接使用&#xff09; npm install terser --save-dev二、在vite.config.ts里面使用 build: {minify: "terser",terserOptions:{compress:{drop_console: true,drop_debugger: true,}}}

Vue3.0(三):Vue组件化深入理解

Vue组件化深入理解 生命周期 每个组件都可能经历 创建、挂载、更新、卸载等一系列过程 在每个阶段&#xff0c;我们可能会添加一些属于自己的逻辑代码 在Vue中&#xff0c;生命周期通过生命周期函数实现 生命周期函数实际上就是回调函数&#xff0c;在某个时间会被Vue源码调…

零售的这个销售模式,太震撼了!

随着科技的飞速发展&#xff0c;新零售模式正逐渐改变着传统零售业的面貌。在这个数字化时代&#xff0c;自动售货机作为新零售的一部分&#xff0c;正以其便捷、智能的特性&#xff0c;为商家和消费者带来全新的购物体验。 客户案例 智能便利店 传统便利店运营面临高租金、人…

【STM32+HAL库+CubeMX】UART轮询收发、中断收发、DMA收发方法及空闲中断详解

&#xff08;转载&#xff09;原文链接&#xff1a;https://blog.csdn.net/qq_39344192/article/details/131470735 1. 什么是UART&#xff1f; UART是一种异步串行通信接口&#xff0c;常用于通过串口与外部设备进行通信。它通过发送和接收数据帧来实现数据传输&#xff0c;使…

保护个人信息安全,避免成为“互联网中的裸泳者”

⚽️ 一、互联网中的裸泳者&#x1f3c0; 二、代理 IP 的应用 - 解锁无限可能⚾️ 三、代理 ip 的几种类型 3.1 动态住宅代理&#xff08;Rotating Residential Proxy&#xff09;3.2 静态住宅代理&#xff08;Static Residential Proxy&#xff09;3.3 动态长效ISP&#xff08…

Nucleosome, Recombinant Human, H2BK120ub1 dNuc, Biotinylated

EpiCypher&#xff08;国内授权代理商欣博盛生物&#xff09;是一家为表观遗传学和染色质生物学研究提供高质量试剂和工具的专业制造商。EpiCypher生产的在E. coli中表达的重组人单核小体(组蛋白H2A、H2B、H3和H4各2个;accession numbers:H2A-P04908;H2B-O60814;H3.1-P68431;H4…

Python实现排序算法

目录 一&#xff1a;快速排序 二&#xff1a;合并排序 三&#xff1a;冒泡排序 四&#xff1a;插入排序 五&#xff1a;选择排序 一&#xff1a;快速排序 def quicksort(arr): if len(arr) < 1: return arr pivot arr[len(arr) // 2] le…

【Docker】入门到精通(常用命令解读)

一、准备工作 1.配置Docker的yum库 首先要安装一个yum工具 yum install -y yum-utils安装成功后&#xff0c;执行命令&#xff0c;配置Docker的yum源&#xff1a; yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo2.安装Docker 执…