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

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

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

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

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

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

题目分析:

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

  • 操作:

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

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

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

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

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

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

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

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

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

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

2. 状态转移方程推导: 为了计算 dp[i],我们思考,这 i 个A是怎么来的? 它必然是从某个状态 dp[j](j 是 i 的因数)通过Copy和Paste得到的。 从 j 个A到 i 个A,需要的操作数是 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

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

为什么这个策略是最优的?因为Copy和Paste的操作,本质上是一种"乘法"增长。而质因数,是构成一个数的最基本的"乘法"单位。我们把 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问题,指引向一个更简单、更本质的数学解法。

咱们下期见~

相关推荐
Navigator_Z2 小时前
LeetCode //C - 1289. Minimum Falling Path Sum II
c语言·算法·leetcode
CCblandy2 小时前
令牌桶在 0.9 倍负载超发 19%、固定窗口边界放行 20 个:四种限流算法同构实测
后端·python·算法·性能优化·架构
YangYang9YangYan3 小时前
2027供应链计划校招:数学与应用数学专业如何把预测模型讲成业务决策?
人工智能·sql·算法
jsjzsl23 小时前
递归自改进系统(RSI)的本质是自发对称破缺:智能爆炸有界—— AI研发切记盲目投入
人工智能·python·算法
程序员清风3 小时前
最短路径算法:BFS、Dijkstra 与 Floyd
java·算法·宽度优先
梦帮科技3 小时前
Kali Linux Scapy 畸形报文构造学——深度解剖非法 TCP Flags 与反常序列号
linux·爬虫·神经网络·算法·数学建模·系统架构·模拟退火算法
171320330计算机毕设编程5 小时前
计算机毕业设计 基于YOLO改进算法的车辆特征识别系统设计与实现--lw
java·大数据·python·sql·算法
姜鱼问生5 小时前
Nginx gzip 压缩:一个配置让页面加载快 3 倍
后端·算法·架构
不会就选b5 小时前
算法日常・每日刷题--<动态规划>23
算法·动态规划