问题描述
小R定义一个数组的“权值”为相邻两数乘积为奇数的对数。给定一个整数 n,表示数组的长度,即需要求从1到n的所有排列的权值之和。每个排列包含从1到n的每个正整数且仅出现一次。由于结果可能非常大,答案需要对 取模
例如:对于数组 [4, 3, 1, 5, 2],有两对相邻元素的乘积为奇数:3 * 1 = 3 和 1 * 5 = 5,因此权值为 2。
解题思路
1. 分析奇数对的数量
首先,数组中的奇数个数为 。相邻两数乘积为奇数,意味着这两个数都是奇数。因此,可能的奇数对数为组合数。
2. 计算每个奇数对在排列中相邻的次数
在所有的排列中,任意两个元素相邻的排列数为 。这是因为将这两个元素视为一个整体,有 种排列方式,再考虑这两个元素之间的顺序,共有 种。
3. 计算权值之和
所有奇数对在所有排列中相邻的总次数为:
需要注意在计算过程中对 取模。
4. 特殊情况处理
当奇数个数 时,权值之和为 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为取模数 。fact用于计算 ,初始值为 1。
-
计算阶乘:
- 使用循环计算 ,在每一步都取模,防止溢出。
-
计算奇数个数 kkk:
- 通过
(n + 1) // 2计算奇数的数量。
- 通过
-
特殊情况判断:
- 当 时,直接返回 0。
-
计算权值之和 SSS:
- 按照推导的公式计算,并在每一步取模。
个人思考和学习建议
在解决这道题的过程中,我深刻体会到数学推导对于算法优化的重要性。初看这道题,可能会尝试枚举所有排列,然后计算权值之和,但显然在 较大时,这种方法不可行。
通过数学推导,我们将问题转化为组合数学问题,大大降低了时间复杂度。这也提醒我,在编程中,算法和数学是密不可分的,掌握基本的数学知识可以帮助我们更高效地解决问题。
对入门同学的建议:
- 加强数学基础: 多学习排列组合、概率统计等数学知识,在算法题中经常会用到。
- 养成推导习惯: 在动手写代码前,先尝试手动推导公式,找到最优解法。
- 注重算法优化: 不要急于求成,先分析时间和空间复杂度,寻找更优的解决方案。
- 多练习多总结: 通过刷题积累经验,但更重要的是在每道题后进行总结,思考是否有更好的方法。
知识总结
使用豆包MarsCode AI刷题的过程中,我学到了如何将复杂的问题简化,并通过数学方法进行求解。同时,也意识到编程不仅仅是代码的实现,更是对问题的理解和分析。
在未来的学习中,我会更加注重:
- 理论与实践相结合: 理论知识是实践的基础,实践又能巩固理论。
- 主动思考: 不盲目依赖现有的解法,尝试提出自己的见解。
- 交流与分享: 与他人讨论可以获得不同的思路,也能加深自己的理解。