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

LeetCode 2654. 使数组所有元素变成 1 的最少操作次数 - GCD 思维题详解

问题分析

这道题乍一看可能会让人想到动态规划或贪心算法,但实际上它更像是一道数学思维题。关键在于理解最大公约数(GCD)的特性和传播规律。

核心洞察

GCD 的关键性质

  1. 1 的特殊地位gcd(1, x) = 1 对任何正整数 x 都成立
  2. 传播性:如果有一个 1,它可以通过操作"感染"相邻的元素
  3. 子数组特性:如果一个子数组的 gcd 为 1,就可以通过操作使整个子数组变成 1

GCD 的结合律性质

GCD 结合律gcd(a, b, c) = gcd(gcd(a, b), c)

这个性质是理解本题的关键!它告诉我们:

  • 即使 gcd(a, b) ≠ 1gcd(b, c) ≠ 1
  • 仍然可能 gcd(a, b, c) = 1

示例[6, 10, 15]

  • gcd(6, 10) = 2gcd(10, 15) = 5
  • 但是 gcd(6, 10, 15) = gcd(gcd(6, 10), 15) = gcd(2, 15) = 1

这就是为什么需要检查整个数组的 gcd,而不仅仅是相邻元素!

欧几里得算法

计算 GCD 的标准方法是欧几里得算法:

算法原理gcd(a, b) = gcd(b, a % b),直到 b = 0

Go 实现

func gcd(a, b int) int {for b != 0 {a, b = b, a % b}return a
}

示例计算

  • gcd(48, 18) = gcd(18, 48 % 18) = gcd(18, 12)
  • gcd(18, 12) = gcd(12, 18 % 12) = gcd(12, 6)
  • gcd(12, 6) = gcd(6, 12 % 6) = gcd(6, 0)
  • gcd(6, 0) = 6

操作的本质理解

题目中的操作实际上是:选择相邻的两个数,用它们的 gcd 替换其中一个。
这相当于我们可以在数组中"传播" gcd 值。

解题思路详解

情况一:数组中已有 1

这是最简单的情况。如果数组中有 count 个 1,那么:

  • 每个非 1 元素只需要 1 次操作就可以变成 1
  • 总操作次数 = 数组长度 - count

示例[1, 3, 6, 1, 4]

  • 有 2 个 1
  • 需要将 3 个非 1 元素变成 1
  • 操作次数 = 5 - 2 = 3

情况二:数组中没有 1,但整体 gcd = 1

如果数组中没有 1,但整个数组的 gcd 为 1,说明我们可以通过操作得到 1。

关键思路:找到最短的子数组,使其 gcd 为 1。

为什么是最短子数组?

  • 长度为 k 的子数组,需要 k-1 次操作来得到第一个 1
  • 得到 1 后,需要 n-1 次操作传播到整个数组
  • 总次数 = (k-1) + (n-1) = n + k - 2
  • 要使总次数最小,就要使 k 最小

情况三:不可能的情况

如果整个数组的 gcd > 1,那么无论如何操作都不可能得到 1,直接返回 -1。

算法实现

寻找最短 gcd=1 子数组的方法

func findShortestSubarray(nums []int) int {n := len(nums)minLen := n + 1 // 初始化为一个不可能的值// 枚举所有子数组for i := 0; i < n; i++ {currentGcd := nums[i]if currentGcd == 1 {return 1 // 找到长度为1的子数组}for j := i + 1; j < n; j++ {currentGcd = gcd(currentGcd, nums[j])if currentGcd == 1 {minLen = min(minLen, j - i + 1)break // 找到了从这个位置开始的最短子数组}}}return minLen
}

完整解题步骤

  1. 统计数组中 1 的个数
  2. 如果有 1,直接返回 n - count
  3. 如果没有 1,检查整个数组的 gcd
  4. 如果整体 gcd > 1,返回 -1
  5. 否则,寻找最短子数组使 gcd = 1
  6. 计算总操作次数:n + minLen - 2

代码实现

func minOperations(nums []int) int {n := len(nums)// 统计1的个数countOnes := 0for _, num := range nums {if num == 1 {countOnes++}}// 情况1:已经有1if countOnes > 0 {return n - countOnes}// 情况2:没有1,检查整体gcdoverallGcd := nums[0]for i := 1; i < n; i++ {overallGcd = gcd(overallGcd, nums[i])}if overallGcd > 1 {return -1 // 情况3:不可能}// 情况4:寻找最短子数组minLen := n + 1for i := 0; i < n; i++ {currentGcd := nums[i]for j := i + 1; j < n; j++ {currentGcd = gcd(currentGcd, nums[j])if currentGcd == 1 {minLen = min(minLen, j - i + 1)break}}}return n + minLen - 2
}func gcd(a, b int) int {for b != 0 {a, b = b, a % b}return a
}func min(a, b int) int {if a < b {return a}return b
}

时间复杂度分析

  • 统计 1 的个数:O(n)
  • 计算整体 gcd:O(n)
  • 寻找最短子数组:O(n²)
  • 总体时间复杂度:O(n²)

由于题目中 n ≤ 50,O(n²) 的解法是完全可以接受的。

关键思考点

  1. 为什么会想到检查整体 gcd?

    • 如果整体 gcd > 1,说明所有数都有共同的因子,无论如何操作都无法消除这个因子
  2. 为什么要找最短子数组?

    • 找到第一个 1 的代价是子数组长度减 1
    • 越短的子数组意味着越早得到 1,总操作次数越少
  3. GCD 的传播特性

    • 一旦得到一个 1,它就可以像"病毒"一样传播到整个数组
    • 每次传播只需要 1 次操作

总结

这道题的核心是理解 GCD 的性质和传播规律。它不是传统的算法题,更像是一道数学思维题。关键在于:

  1. 理解 1 在 GCD 运算中的特殊地位
  2. 认识到 GCD 的传播特性
  3. 通过找最短子数组来优化操作次数

通过这道题,我们可以看到算法题有时更需要数学思维,而不是死板的算法模板。

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

相关文章:

  • 站长统计芭乐官方网站下载wordpress移动主题开发教程
  • 网站开发大学是什么专业营销型企业网站系统模板下载
  • JavaScript 35个数组方法完整参数返回值表
  • PPP协议异界冒险:连接神殿的试炼
  • 网站建设的基本条件网站制作协议
  • kotlin build.gradle.kts下修改APK的输出名称
  • 帝国cms地方门户网站模板室内设计效果图素材网站
  • 淘客网站怎么建设小程序商城货源怎么找
  • 在线编译C语言:提升跨平台开发效率
  • 诊断数据库 --- ODX和PDX关系核区别
  • 阿里云申请域名后网站转运网站建设
  • 第13章 函数式语言特性
  • C语言防止反编译工具 | 提高程序安全性并保护源代码
  • 【实战】动态 SQL + 统一 Result + 登录校验:图书管理系统(下)
  • 双缓冲机制:如何避免读写冲突
  • C语言在线编译器网站 | 高效便捷的编程学习平台
  • 你的技术搭子在这里!来openFuyao社区SIG与大咖一起组队
  • 台州自助建站系统做兼职上哪个网站
  • 旅行社应做哪些网站做家电维修网站
  • 网站加入购物车的代码网站视频链接怎么做
  • atsec完成Newland NPT的P2PE PA评估
  • 网站推广预算在线网页爬虫工具
  • 从0开始学区块链第15天——发送和接受ETH
  • 如何批量建站在本地做的网站怎么修改域名
  • 崇左网站搭建给网站开发APP
  • C语言的编译器 | 如何选择适合你的编译器
  • 上位机项目列表
  • 如何用电脑记事本做网站别墅装修设计图片大全 效果图
  • 销售怎么做英文网站建设优化
  • Mac编译C语言 | 学会在Mac上使用终端编译和运行C语言程序