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

[动态规划]1900. 最佳运动员的比拼回合

1900. 最佳运动员的比拼回合

1900. 最佳运动员的比拼回合 - 力扣(LeetCode)

问题理解

  • 比赛规则​:每轮比赛将选手两两配对比赛,胜者晋级下一轮。如果人数为奇数,最后一人自动晋级。
  • 目标​:计算选手A和选手B最早和最晚会在第几轮相遇。

解决方案

主要思路

使用深度优先搜索(DFS)结合记忆化来递归计算所有可能的比赛情况,找出最早和最晚的相遇轮次。

关键函数:dfs(n, first, second)

这个递归函数计算在当前有n个选手的情况下,选手A(位置first)和选手B(位置second)相遇的最早和最晚轮次。

参数说明
  • n: 当前轮次的选手总数
  • first: 选手A的位置(1-based)
  • second: 选手B的位置(1-based)
返回值

返回一个元组(earliest, latest),表示最早和最晚相遇轮次。

代码

class Solution:def earliestAndLatest(self, n: int, firstPlayer: int, secondPlayer: int) -> List[int]:@cache #缓存装饰器,避免重复计算dfs(一行代码实现记忆化)def dfs(n:int, first: int, second: int) -> Tuple[int,int]:# 配对时,位置 i 的选手会和位置 n + 1 - i 的选手比赛。# 当两个选手的位置满足first + second == n + 1时,说明他们会在当前轮次相遇if first + second == n+1:return 1, 1#为了减少计算量,我们始终保持选手A在左侧#注释:题目已保证first < secondif first + second > n + 1:first, second = n + 1 - second, n + 1 - first#下一轮的人数m是当前人数加1除以2的整数部分m = (n + 1) // 2# 两选手间保留[min_mid, max_mid]个人# 计算两个选手之间可能保留的中间选手数量范围min_mid = 0 if second <= m else second - n // 2 - 1max_mid = second - first if second <= m else m - firstearliest, latest = inf, 0# 枚举所有可能情况# 通过双重循环枚举:# 选手A左侧保留的选手数量left# 选手A和B之间保留的选手数量mid# 对于每种情况,递归计算下一轮的结果for left in range(first):for mid in range(min_mid, max_mid):e, l = dfs(m, left + 1, left + mid + 2)earliest = min(earliest, e)latest = max(latest, l)#将当前轮次加上递归结果return earliest + 1, latest + 1return list(dfs(n, firstPlayer, secondPlayer))# ​时间复杂度​:由于使用了记忆化,避免了重复计算,复杂度约为O(n^4)
# ​空间复杂度​:主要取决于递归深度和缓存大小,约为O(n^3)

1、相遇的条件

  • 如果选手A的位置是 first,选手B的位置是 second,那么:
    • 选手A的对手位置是 n + 1 - first
    • 选手B的对手位置是 n + 1 - second

2、first + second > n + 1 的代码能保证选手A在左侧

它确保选手A(first)的位置总是在左侧(即更靠近起点),而选手B(second)的位置总是在右侧(即更靠近终点)

  • 问题描述中已经保证 first < second(选手A的位置在选手B的左侧)。
  • 但直接递归计算时,可能会遇到选手A和选手B的位置分布不对称的情况(比如选手A靠近右侧,选手B靠近左侧)。
  • 通过对称性处理,我们可以将问题统一为“选手A在左侧,选手B在右侧”的形式,避免重复计算。

对称性处理的逻辑

  • 如果 first + second > n + 1,说明选手A和选手B的位置分布偏向右侧:
    • 比如 n = 5first = 4second = 5
      • first + second = 9 > 6 = n + 1
      • 此时选手A(4)和选手B(5)都靠近右侧。
  • 通过对称性转换:
    • first' = n + 1 - second = 6 - 5 = 1
    • second' = n + 1 - first = 6 - 4 = 2
    • 新的 first' = 1 和 second' = 2 表示选手A和选手B在对称位置的左侧。
    • 这样问题就转换为“选手A在左侧,选手B在右侧”的标准形式。
    • 因为 first < second,所以 first' > second' 不成立(因为 n + 1 - second < n + 1 - first)。
    • 因此,转换后的 first' 仍然小于 second',且 first' 更靠近左侧。

3、计算 min_mid 和 max_mid

计算 min_mid

min_mid 表示两个选手之间至少保留的中间选手数量:

  • 如果 second <= m(选手B在左侧半区):
    • 中间选手数量可以为 0(即两个选手之间没有其他人)。
    • 因此 min_mid = 0
  • 如果 second > m(选手B在右侧半区):
    • 选手B在下一轮的位置会映射到 second - n // 2 - 1
    • 因此 min_mid = second - n // 2 - 1

计算 max_mid

max_mid 表示两个选手之间最多保留的中间选手数量:

  • 如果 second <= m(选手B在左侧半区):
    • 中间选手数量最多为 second - first - 1(即两个选手之间的所有选手)。
    • 因此 max_mid = second - first(因为 mid 的取值范围是左闭右开区间)。
  • 如果 second > m(选手B在右侧半区):
    • 下一轮的选手数量是 m,选手A的位置最多可以保留 m - first 个中间选手。
    • 因此 max_mid = m - first

枚举 mid 的意义

  • mid 表示两个选手之间保留的中间选手数量。
  • 通过枚举 mid 的所有可能值(从 min_mid 到 max_mid - 1),我们可以覆盖所有可能的比赛配对情况。
  • 对于每个 mid,递归计算下一轮的最早和最晚相遇轮次。

http://www.dtcms.com/a/276313.html

相关文章:

  • Matplotlib 模块入门
  • 非欧几里得空间图卷积算子设计:突破几何限制的图神经网络新范式
  • Linux系统中部署Redis详解
  • python作业2
  • 【时间之外】AI在农机配件设计场景的应用
  • 【详解ProTable源码】高级筛选栏如何实现一行五列
  • Elasticsearch 的 `modules` 目录
  • AMD 锐龙 AI MAX+ 395 处理器与端侧 AI 部署的行业实践
  • 【华为OD】MVP争夺战2(C++、Java、Python)
  • 拼多多电商运营技巧---价格体系设置
  • 数据的计算与格式化
  • [Python] Flask 多线程绘图时报错“main thread is not in main loop”的解决方案
  • SuperClaude命令参考手册:AI编程革命中的20个核心指令详解
  • JavaEE多线程——锁策略 CAS synchronized优化
  • UI前端大数据可视化新探索:如何利用色彩心理学提升数据传达效果?
  • [vroom] 启发式算法(路径评估) | 局部搜索优化引擎 | 解决方案输出解析
  • 单向链表反转 如何实现
  • 蓝牙BT UUID的含义以及使用方法案例说明
  • 第十八天,7月12日,八股
  • 【MySQL笔记】事务的ACID特性与隔离级别
  • 动态规划基本操作
  • AutoGen框架官方文档梳理-完整学习指南
  • Java中的方法传参机制
  • 【工程数学基础】条件极值与拉格朗日乘数法
  • uniapp弹出手机键盘,布局被顶飞,导致页面混乱问题
  • 使用包管理工具CocoaPods、SPM、Carthage的利弊与趋势
  • C#与FX5U进行Socket通信
  • 数据结构之并查集和LRUCache
  • OGC:开放地理空间联盟简介
  • YOLO家族内战!v5/v8/v10谁才是你的真命天子?(附保姆级选择指南)