算法题题解记录——双变量问题的 “枚举右,维护左”

算法题的时间复杂度是必须考虑的因素,n的数量级和复杂度二者可以决定算法的执行时间。在有进行时间限制的题中,一个过于复杂的题解会因超时而得不到全分。

本文记录了对观光景点组合得分问题的思考、分析和优化。

题目

www.marscode.cn/practice/r3...

方法一:BF

思路

这道题的暴力解法是采用 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n 2 ) O(n^2) </math>O(n2) 的遍历,枚举所有的两两组合,取最大值。

代码

python 复制代码
def solution(values: list) -> int:
    # PLEASE DO NOT MODIFY THE FUNCTION SIGNATURE
    # write code here

    ans = 0
    l = len(values)
    for i in range(l):
        for j in range(i + 1, l):
            ans = max(ans, values[i] + values[j] + i - j)

    return ans


if __name__ == '__main__':
    print(solution(values=[8, 3, 5, 5, 6]) == 11)
    print(solution(values=[10, 4, 8, 7]) == 16)
    print(solution(values=[1, 2, 3, 4, 5]) == 8)

复杂度

  • 时间复杂度 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n 2 ) O(n^2) </math>O(n2)
  • 空间复杂度 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1)

思考优化点

这道题是leetcode第1014题,在力扣中明确提到了n最大可以取到 <math xmlns="http://www.w3.org/1998/Math/MathML"> 5 ∗ 1 0 4 5 * 10^4 </math>5∗104。而 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n 2 ) O(n^2) </math>O(n2) 的算法会执行 <math xmlns="http://www.w3.org/1998/Math/MathML"> 25 ∗ 1 0 8 25*10^8 </math>25∗108,即 <math xmlns="http://www.w3.org/1998/Math/MathML"> 2.5 ∗ 1 0 9 2.5*10^9 </math>2.5∗109 次,而现代处理器大约能够每秒执行 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 0 8 10^8 </math>108 到 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 0 9 10^9 </math>109 次基本操作。 因此通常认为如果超过 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 0 9 10^9 </math>109 则超时

既然超时,那就一定存在不必要的重复计算,暴力求解的方法哪里重复计算了呢?请看图:

我们对 values[i] + values[j] + i - j 重新组合为values[i] + i + values[j] - j,会发现:对于固定的ji < j,只要 values[i] + i 越大,结果越大。而当 j 移动到 j+1 时,前 j - 1value[i] + i 的最大值已经计算过了。

显然这里可以优化。

方法二:

思路

既然左侧的 values[i] + i 存在重复计算,那显然我们可以用变量将其保存起来。然后在单次循环中进行最大值的维护。

这个思路被叫做双变量问题 的 "枚举右,维护左"。详见文章末尾。

代码

python 复制代码
def solution(values: list) -> int:
    # PLEASE DO NOT MODIFY THE FUNCTION SIGNATURE
    # write code here
    ans = 0
    mx = values[0] + 0
    for j in range(1, len(values)):
        ans = max(ans, mx + values[j] - j)
        # 边遍历边维护
        mx = max(mx, values[j] + j)
    return ans


if __name__ == '__main__':
    print(solution(values=[8, 3, 5, 5, 6]) == 11)
    print(solution(values=[10, 4, 8, 7]) == 16)
    print(solution(values=[1, 2, 3, 4, 5]) == 8)

复杂度

  • 时间复杂度 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)
  • 空间复杂度 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1)

总结

由数据范围反推算法复杂度以及算法内容

一般ACM或者笔试题的时间限制是1秒或2秒。 在这种情况下,C++代码的操作次数控制在10⁷~10⁸为最佳。 下面给出在不同数据范围内,代码的时间复杂度和算法该如何选择:

  1. n ≤ 30,指数级别,dfs+剪枝,状态压缩dp
  2. n ≤ 100,= O(n³),floyd,dp,高斯消元
  3. n ≤ 1000,= O(n²),O(n²logn),dp,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford
  4. n ≤ 10000,= O(n * √n),块状链表、分块、莫队
  5. n ≤ 100000,= O(nlogn),各种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、Kruskal、spfa、求凸包、求半平面交、二分、CDQ分治、整体二分、后缀数组、树链剖分、动态树
  6. n ≤ 1000000 => O(n),以及常数较小的O(nlogn) 算法 => 单调队列、hash、双指针扫描、BFS、并查集、kmp、AC自动机,常数较小的O(nlogn)的做法: sort、树状数组、heap、dijkstra、spfa
  7. n ≤ 10000000,= O(n),双指针扫描、kmp、AC自动机、线性素数筛
  8. n ≤ 10⁹ => O(√n),判断质数
  9. n ≤ 10¹⁸ => O(logn),最大公约数,快速幂,数位DP
  10. n ≤ 10¹⁰⁰⁰ => O((logn)²),高精度加减乘除
  11. n ≤ 10¹⁰⁰⁰⁰⁰ => O(logk × loglogk),k表示位数,高精度加减、FFT/NTT

枚举右,维护左

对于 双变量问题 ,例如两数之和 aᵢ + aⱼ = t,可以枚举右边的 aⱼ,转换成 单变量问题 ,也就是在 aⱼ 左边查找是否有 aᵢ = t - aⱼ,这可以用哈希表维护。 这个技巧叫做 枚举右,维护左

参考文章:

www.acwing.com/blog/conten...

leetcode.cn/circle/disc...

相关推荐
一道微光6 分钟前
Mac的M2芯片运行lightgbm报错,其他python包可用,x86_x64架构运行
开发语言·python·macos
四口鲸鱼爱吃盐34 分钟前
Pytorch | 利用AI-FGTM针对CIFAR10上的ResNet分类器进行对抗攻击
人工智能·pytorch·python
是娜个二叉树!42 分钟前
图像处理基础 | 格式转换.rgb转.jpg 灰度图 python
开发语言·python
互联网杂货铺1 小时前
Postman接口测试:全局变量/接口关联/加密/解密
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·postman
程序员老冯头2 小时前
第十五章 C++ 数组
开发语言·c++·算法
南七澄江2 小时前
各种网站(学习资源及其他)
开发语言·网络·python·深度学习·学习·机器学习·ai
无泡汽水3 小时前
漏洞检测工具:Swagger UI敏感信息泄露
python·web安全
暮暮七3 小时前
理想很丰满的Ollama-OCR
linux·python·大模型·ocr·markdown·ollama
ai_lian_shuo4 小时前
四、使用langchain搭建RAG:金融问答机器人--构建web应用,问答链,带记忆功能
python·ai·金融·langchain·机器人