LeetCode——1900. 最佳运动员的比拼回合(The Earliest and Latest Rounds Where Players Compete)[困难]——分析及代码(Java)

LeetCode——1900. 最佳运动员的比拼回合[The Earliest and Latest Rounds Where Players Compete][困难]——分析及代码[Java]

一、题目

n 名运动员参与一场锦标赛,所有运动员站成一排,并根据 最开始的 站位从 1 到 n 编号(运动员 1 是这一排中的第一个运动员,运动员 2 是第二个运动员,依此类推)。

锦标赛由多个回合组成(从回合 1 开始)。每一回合中,这一排从前往后数的第 i 名运动员需要与从后往前数的第 i 名运动员比拼,获胜者将会进入下一回合。如果当前回合中运动员数目为奇数,那么中间那位运动员将轮空晋级下一回合。

  • 例如,当前回合中,运动员 1, 2, 4, 6, 7 站成一排
    • 运动员 1 需要和运动员 7 比拼
    • 运动员 2 需要和运动员 6 比拼
    • 运动员 4 轮空晋级下一回合

每回合结束后,获胜者将会基于最开始分配给他们的原始顺序(升序)重新排成一排。

编号为 firstPlayer 和 secondPlayer 的运动员是本场锦标赛中的最佳运动员。在他们开始比拼之前,完全可以战胜任何其他运动员。而任意两个其他运动员进行比拼时,其中任意一个都有获胜的可能,因此你可以 裁定 谁是这一回合的获胜者。

给你三个整数 n、firstPlayer 和 secondPlayer 。返回一个由两个值组成的整数数组,分别表示两位最佳运动员在本场锦标赛中比拼的 最早 回合数和 最晚 回合数。

示例 1:

输入:n = 11, firstPlayer = 2, secondPlayer = 4
输出:[3,4]
解释:
一种能够产生最早回合数的情景是:
回合 1:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
回合 2:2, 3, 4, 5, 6, 11
回合 3:2, 3, 4
一种能够产生最晚回合数的情景是:
回合 1:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
回合 2:1, 2, 3, 4, 5, 6
回合 3:1, 2, 4
回合 4:2, 4

示例 2:

输入:n = 5, firstPlayer = 1, secondPlayer = 5
输出:[1,1]
解释:两名最佳运动员 1 和 5 将会在回合 1 进行比拼。
不存在使他们在其他回合进行比拼的可能。

提示:

  • 2 <= n <= 28
  • 1 <= firstPlayer < secondPlayer <= n

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/the-earliest-and-latest-rounds-where-players-compete
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、分析及代码

1. 记忆化搜索

(1)思路

本题分析过程较复杂,官方题解写得很好,可参考:最佳运动员的比拼回合

(2)代码

class Solution {
    int[][][] minRound, maxRound;//记忆化

    public int[] earliestAndLatest(int n, int firstPlayer, int secondPlayer) {
        this.minRound = new int[n + 1][n + 1][n + 1];
        this.maxRound = new int[n + 1][n + 1][n + 1];

        if (firstPlayer < secondPlayer)//调整使搜索时保持s<f,简化代码
            return dp(n, firstPlayer, secondPlayer);
        return dp(n, secondPlayer, firstPlayer);
    }

    public int[] dp(int n, int f, int s) {
        if (minRound[n][f][s] > 0)//已记忆化
            return new int[]{minRound[n][f][s], maxRound[n][f][s]};
        if (f + s == n + 1)//最后一回合,f和s比拼
            return new int[]{1, 1};
        if (f + s > n + 1) {//调整使搜索时保持s<f,简化代码
            int[] res = dp(n, n + 1 - s, n + 1 - f);
            minRound[n][f][s] = res[0];
            maxRound[n][f][s] = res[1];
            return res;
        }

        int minRes = Integer.MAX_VALUE, maxRes = Integer.MIN_VALUE;//当前情况下最小、最大回合计算结果
        int half = (n + 1) >> 1;//中间值,下一回合人数

        if (s <= half) {//s在左侧或中间
            for (int i = 0; i < f; i++) {//f左侧人数
                for (int j = 0; j < s - f; j++) {//f和s之间的人数
                    int[] res = dp(half, i + 1, i + j + 2);//状态转移
                    minRes = Math.min(minRes, res[0] + 1);
                    maxRes = Math.max(maxRes, res[1] + 1);
                }
            }
        } else {//s在右侧
            for (int i = 0; i < f; i++) {//f左侧人数
                for (int j = 0; j < n + 1 - s - f; j++) {//f和s'之间的人数
                    int[] res = dp(half, i + 1, i + j + 2 + (s * 2 - n - 1) / 2);//状态转移
                    minRes = Math.min(minRes, res[0] + 1);
                    maxRes = Math.max(maxRes, res[1] + 1);
                }
            }
        }

        minRound[n][f][s] = minRes;//记忆化
        maxRound[n][f][s] = maxRes;//记忆化
        return new int[]{minRes, maxRes};
    }
}

(3)结果

执行用时 :4 ms,在所有 Java 提交中击败了 100.00% 的用户;
内存消耗 :38.8 MB,在所有 Java 提交中击败了 100.00% 的用户。
(目前提交用户量不足,暂无排名)

三、其他

暂无。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值