动态规划的“数学之魂”:从DP推演到质因数分解——巧解「只有两个键的键盘」

哈喽,各位,我是前端小L。

我们的DP之旅,已经探索了序列、矩阵、树上的各种问题,它们大多是"分析型"的------在一个给定的结构上寻找最优解。今天,我们将再次回到"构造型"DP的世界,我们的任务,是从一个A开始,通过"复制"和"粘贴",以最快的速度,"生产"出nA

这道题,是DP与初等数论结合的绝佳典范。它将向我们展示,动态规划的递推关系,有时会惊人地揭示出问题背后隐藏的数学本质。

力扣 650. 只有两个键的键盘

https://leetcode.cn/problems/2-keys-keyboard/

题目分析:

  • 初始状态 : 屏幕上有1个A

  • 操作:

    1. Copy All: 复制屏幕上所有的A到剪贴板。

    2. Paste: 将剪贴板的内容粘贴到屏幕上。

  • 目标 : 得到恰好 nA,所需要的最少操作次数。

核心洞察: Paste操作的次数,取决于你上一次Copy时屏幕上有多少个A

  • 如果你上次在有 jA时按下了Copy,那么每次Paste,屏幕上的A就会增加 j 个。

  • 要从 jA变成 iA,你需要1次Copy ,和 (i/j - 1)Paste。总共需要 1 + (i/j - 1) = i/j 次操作。

  • 这也意味着,j 必须是 i 的一个因数

思路一:动态规划的"正统"之路 (O(n²))

这个"因数"关系,就是我们构建DP的基石。

1. DP状态定义: dp[i] 表示:得到 iA所需要的最少操作次数。

2. 状态转移方程推导: 为了计算 dp[i],我们思考,这 iA是怎么来的? 它必然是从某个状态 dp[j]ji 的因数)通过CopyPaste得到的。 从 jAiA,需要的操作数是 i/j 次。 所以,通过这条路径得到的总操作数是 dp[j] + i/j

但是,i 可能有很多因数 j。我们应该从哪个 j 转移过来呢? 为了让 dp[i] 最小,我们必须遍历所有 i 的因数 j ,然后取所有 dp[j] + i/j 结果中的最小值。

于是,我们得到了状态转移方程: dp[i] = min_{j | i, j < i} { dp[j] + i/j }

3. Base Case: dp[1] = 0。我们一开始就有1个A,不需要任何操作。

代码实现 (DP)

复制代码
class Solution {
public:
    int minSteps(int n) {
        if (n == 1) return 0;
        vector<int> dp(n + 1);
        dp[1] = 0;

        for (int i = 2; i <= n; ++i) {
            dp[i] = i; // 初始化为最坏情况:从1个A复制,粘贴i-1次,共i步
            // 遍历 i 的所有因数 j (从大到小或从小到大都可以)
            for (int j = i / 2; j >= 1; --j) {
                if (i % j == 0) {
                    dp[i] = min(dp[i], dp[j] + i / j);
                }
            }
        }
        return dp[n];
    }
};

这个解法是正确的,但效率不高。它引导我们走向了更深层次的思考。

思路二:"Aha!"时刻------DP关系揭示的数学本质

让我们仔细审视 dp[i] = dp[j] + i/j。 如果 i = a * b,那么 dp(a*b) = dp(a) + b (假设从a转移)。

现在,让我们考虑 n质因数分解 。 假设 n = p1 * p2 * p3 (p1, p2, p3 都是质数)。

  • dp(n) = dp(p1 * p2 * p3)

  • 为了得到 n,我们可以先得到 p1 * p2,然后再乘以 p3

  • 那么,dp(n) = dp(p1 * p2) + p3

  • 同理,dp(p1 * p2) = dp(p1) + p2

  • dp(p1) 是多少?因为 p1 是质数,它的唯一因数是1。所以 dp(p1) = dp(1) + p1/1 = 0 + p1 = p1

把它们串起来: dp(n) = dp(p1 * p2) + p3 = (dp(p1) + p2) + p3 = p1 + p2 + p3

一个惊人的结论诞生了:得到 nA的最少操作次数,恰好等于 n 的所有质因数之和!

为什么这个策略是最优的?因为CopyPaste的操作,本质上是一种"乘法"增长。而质因数,是构成一个数的最基本的"乘法"单位。我们把 n 分解成最基本的乘法单元,然后一步步地"乘"上去,这自然就是最经济的路径。

最终解法:质因数分解 (O(sqrt(n)))

现在,问题被彻底"降维"成了一个纯粹的数学问题:求 n 的质因数之和。

算法流程:

  1. 初始化 ans = 0

  2. i = 2 开始循环,i*i <= n: a. 如果 n 能被 i 整除,说明 i 是一个质因数。 b. 我们就把 i 加到 ans 中,然后把 n 除以 i,继续检查 i 是否还是 n 的因数。

  3. 循环结束后,如果 n > 1,说明 n 本身就是一个大的质因数,把它也加到 ans 中。

代码实现 (数学解法)

复制代码
class Solution {
public:
    int minSteps(int n) {
        int ans = 0;
        for (int i = 2; i * i <= n; ++i) {
            while (n % i == 0) {
                ans += i;
                n /= i;
            }
        }
        if (n > 1) {
            ans += n;
        }
        return ans;
    }
};

总结:DP是发现规律的"显微镜"

今天这道题,是DP学习之旅中一次极其深刻和美妙的体验。它向我们展示了:

动态规划,不仅是一种求解工具,更是一种强大的分析工具。它能像一台"显微镜",帮助我们洞察问题内部的结构和规律,有时甚至能将一个复杂的DP问题,指引向一个更简单、更本质的数学解法。

咱们下期见~

相关推荐
业精于勤的牙23 分钟前
三角形最小路径和(二)
算法
风筝在晴天搁浅24 分钟前
hot100 239.滑动窗口最大值
数据结构·算法·leetcode
夏乌_Wx36 分钟前
练题100天——DAY31:相对名次+数组拆分+重塑矩阵
数据结构·算法
LYFlied36 分钟前
【算法解题模板】-解二叉树相关算法题的技巧
前端·数据结构·算法·leetcode
Ven%1 小时前
【AI大模型算法工程师面试题解析与技术思考】
人工智能·python·算法
天勤量化大唯粉1 小时前
枢轴点反转策略在铜期货中的量化应用指南(附天勤量化代码)
ide·python·算法·机器学习·github·开源软件·程序员创富
爱学习的小仙女!1 小时前
算法效率的度量 时间复杂度 空间复杂度
数据结构·算法
AndrewHZ1 小时前
【复杂网络分析】什么是图神经网络?
人工智能·深度学习·神经网络·算法·图神经网络·复杂网络
Swizard1 小时前
拒绝“狗熊掰棒子”!用 EWC (Elastic Weight Consolidation) 彻底终结 AI 的灾难性遗忘
python·算法·ai·训练
fab 在逃TDPIE2 小时前
Sentaurus TCAD 仿真教程(十)
算法