小U的数组权值计算 | 豆包MarsCode AI刷题

82 阅读3分钟

问题描述

小R定义一个数组的“权值”为相邻两数乘积为奇数的对数。给定一个整数 n,表示数组的长度,即需要求从1到n的所有排列的权值之和。每个排列包含从1到n的每个正整数且仅出现一次。由于结果可能非常大,答案需要对109+710^9+7 取模

例如:对于数组 [4, 3, 1, 5, 2],有两对相邻元素的乘积为奇数:3 * 1 = 3 和 1 * 5 = 5,因此权值为 2。

解题思路

1. 分析奇数对的数量

首先,数组中的奇数个数为 𝑘=n12𝑘=⌊\dfrac{n-1}{2}⌋。相邻两数乘积为奇数,意味着这两个数都是奇数。因此,可能的奇数对数为组合数C(k,2)=k(k1)2C(k, 2) = \dfrac{k(k-1)}{2}

2. 计算每个奇数对在排列中相邻的次数

在所有的排列中,任意两个元素相邻的排列数为 2×(n1)!2 \times (n - 1)!。这是因为将这两个元素视为一个整体,有 (n1)!(n - 1)! 种排列方式,再考虑这两个元素之间的顺序,共有2×(n1)!2 \times (n - 1)! 种。

3. 计算权值之和

所有奇数对在所有排列中相邻的总次数为:

S=C(k,2)×2×(n1)!=k(k1)×(n1)!S = C(k, 2) \times 2 \times (n - 1)! = k(k - 1) \times (n - 1)!

需要注意在计算过程中对 109+710^9+7 取模。

4. 特殊情况处理

当奇数个数 k<2k < 2 时,权值之和为 0,因为没有足够的奇数组成一对。

代码实现

python
复制代码
def solution(n: int) -> int:
    mod = 10**9 + 7
    if n < 2:
        return 0
    # 计算 (n - 1)! 模 mod
    fact = 1
    for i in range(2, n):
        fact = (fact * i) % mod
    k = (n + 1) // 2  # 奇数的个数
    if k < 2:
        return 0
    # 计算权值之和
    s = fact * k % mod * (k - 1) % mod
    return s

if __name__ == '__main__':
    print(solution(5))  # 输出 144
    print(solution(3))  # 输出 4
    print(solution(6))  # 输出 720

代码详解

  • 变量初始化:

    • mod 为取模数 109+710^9+7
    • fact 用于计算 (n1)!(n - 1)!,初始值为 1。
  • 计算阶乘:

    • 使用循环计算 (n1)!(n - 1)!,在每一步都取模,防止溢出。
  • 计算奇数个数 kkk:

    • 通过 (n + 1) // 2 计算奇数的数量。
  • 特殊情况判断:

    • k<2k < 2 时,直接返回 0。
  • 计算权值之和 SSS:

    • 按照推导的公式计算,并在每一步取模。

个人思考和学习建议

在解决这道题的过程中,我深刻体会到数学推导对于算法优化的重要性。初看这道题,可能会尝试枚举所有排列,然后计算权值之和,但显然在 nn 较大时,这种方法不可行。

通过数学推导,我们将问题转化为组合数学问题,大大降低了时间复杂度。这也提醒我,在编程中,算法和数学是密不可分的,掌握基本的数学知识可以帮助我们更高效地解决问题。

对入门同学的建议:

  1. 加强数学基础: 多学习排列组合、概率统计等数学知识,在算法题中经常会用到。
  2. 养成推导习惯: 在动手写代码前,先尝试手动推导公式,找到最优解法。
  3. 注重算法优化: 不要急于求成,先分析时间和空间复杂度,寻找更优的解决方案。
  4. 多练习多总结: 通过刷题积累经验,但更重要的是在每道题后进行总结,思考是否有更好的方法。

知识总结

使用豆包MarsCode AI刷题的过程中,我学到了如何将复杂的问题简化,并通过数学方法进行求解。同时,也意识到编程不仅仅是代码的实现,更是对问题的理解和分析。

在未来的学习中,我会更加注重:

  • 理论与实践相结合: 理论知识是实践的基础,实践又能巩固理论。
  • 主动思考: 不盲目依赖现有的解法,尝试提出自己的见解。
  • 交流与分享: 与他人讨论可以获得不同的思路,也能加深自己的理解。