动态规划-整数拆分(集合角度推导)

题目

整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。

1. 集合定义

对于一个整数 ( n ),将其拆分的所有方式记为集合 ( S(n) ):
S ( n ) = { ( a 1 , a 2 , … , a k ) ∣ a 1 + a 2 + ⋯ + a k = n , a i > 0 , k ≥ 2 } S(n) = \{(a_1, a_2, \dots, a_k) \mid a_1 + a_2 + \cdots + a_k = n, \, a_i > 0, \, k \geq 2\} S(n)={(a1,a2,,ak)a1+a2++ak=n,ai>0,k2}

每种拆分方式对应一个乘积:
P ( a 1 , a 2 , … , a k ) = a 1 × a 2 × ⋯ × a k P(a_1, a_2, \dots, a_k) = a_1 \times a_2 \times \cdots \times a_k P(a1,a2,,ak)=a1×a2××ak

我们需要在这些拆分方式中找到乘积最大的那种,记为 d p [ n ] dp[n] dp[n]
d p [ n ] = max ⁡ ( a 1 , a 2 , … , a k ) ∈ S ( n ) P ( a 1 , a 2 , … , a k ) dp[n] = \max_{(a_1, a_2, \dots, a_k) \in S(n)} P(a_1, a_2, \dots, a_k) dp[n]=(a1,a2,,ak)S(n)maxP(a1,a2,,ak)


2. 集合拆解

考虑将整数 n n n 拆分为两部分 j j j n − j n-j nj,其中 j j j是拆分点:
n = j + ( n − j ) n = j + (n-j) n=j+(nj)

此时可以将 S ( n ) S(n) S(n)拆解为两类子集合:

  1. 直接拆分:只将 n n n 拆分为两部分 j j j n − j n-j nj,对应的乘积为:
    P ( j , n − j ) = j × ( n − j ) P(j, n-j) = j \times (n-j) P(j,nj)=j×(nj)

  2. 递归拆分:进一步对 n − j n-j nj继续拆分,对应的乘积为:
    P ( j , S ( n − j ) ) = j × d p [ n − j ] P(j, S(n-j)) = j \times dp[n-j] P(j,S(nj))=j×dp[nj]

因此,集合 S ( n ) S(n) S(n)的最大值可以通过以下公式表示:
d p [ n ] = max ⁡ 1 ≤ j < n { j × ( n − j ) , j × d p [ n − j ] } dp[n] = \max_{1 \leq j < n} \{j \times (n-j), j \times dp[n-j]\} dp[n]=1j<nmax{j×(nj),j×dp[nj]}


3. 动态规划状态转移公式

通过上述分析,可以总结出状态转移公式:
d p [ n ] = max ⁡ 1 ≤ j < n { max ⁡ ( j × ( n − j ) , j × d p [ n − j ] ) } dp[n] = \max_{1 \leq j < n} \{\max(j \times (n-j), j \times dp[n-j])\} dp[n]=1j<nmax{max(j×(nj),j×dp[nj])}

其中:

  • j × ( n − j ) j \times (n-j) j×(nj) 表示不递归拆分,直接将 n n n 拆为 j j j n − j n-j nj
  • j × d p [ n − j ] j \times dp[n-j] j×dp[nj] 表示递归使用之前的最优解。

4. 初始化条件

动态规划需要初始值:

  • d p [ 1 ] = 1 dp[1] = 1 dp[1]=1(虽然通常不拆分,但作为递归的基准)。
  • d p [ 2 ] = 1 dp[2] = 1 dp[2]=1(2 拆分为 1 和 1,乘积为 1)。

5. 集合视角的递推步骤

从小到大递推:

  1. 对于每个 n n n,枚举所有可能的拆分点 j j j
  2. 计算拆分后的两个子问题:
    • 不再拆分的乘积: j × ( n − j ) j \times (n-j) j×(nj)
    • 继续递归的乘积: j × d p [ n − j ] j \times dp[n-j] j×dp[nj]
  3. 在所有拆分中找到最大值,赋值给 d p [ n ] dp[n] dp[n]

示例推导 $n = 5 )

集合拆分视角分析:

  • S ( 5 ) S(5) S(5):所有可能的拆分:
    S ( 5 ) = { ( 4 , 1 ) , ( 3 , 2 ) , ( 3 , 1 , 1 ) , ( 2 , 2 , 1 ) , ( 2 , 1 , 1 , 1 ) , ( 1 , 1 , 1 , 1 , 1 ) } S(5) = \{(4, 1), (3, 2), (3, 1, 1), (2, 2, 1), (2, 1, 1, 1), (1, 1, 1, 1, 1)\} S(5)={(4,1),(3,2),(3,1,1),(2,2,1),(2,1,1,1),(1,1,1,1,1)}
  • 最大乘积来自以下两种情况:
    1. 直接拆分为两部分,枚举 j = 1 , 2 , 3 , 4 j = 1, 2, 3, 4 j=1,2,3,4
    2. 递归拆分,利用 d p [ n − j ] dp[n-j] dp[nj]

动态规划计算:

  1. 初始化: d p [ 1 ] = 1 dp[1] = 1 dp[1]=1 d p [ 2 ] = 1 dp[2] = 1 dp[2]=1
  2. 递推:
    • d p [ 3 ] = max ⁡ ( 1 × 2 , 1 × d p [ 2 ] ) = 2 dp[3] = \max(1 \times 2, 1 \times dp[2]) = 2 dp[3]=max(1×2,1×dp[2])=2
    • d p [ 4 ] = max ⁡ ( 1 × 3 , 1 × d p [ 3 ] , 2 × 2 ) = 4 dp[4] = \max(1 \times 3, 1 \times dp[3], 2 \times 2) = 4 dp[4]=max(1×3,1×dp[3],2×2)=4
    • d p [ 5 ] = max ⁡ ( 1 × 4 , 1 × d p [ 4 ] , 2 × 3 , 2 × d p [ 3 ] ) = 6 dp[5] = \max(1 \times 4, 1 \times dp[4], 2 \times 3, 2 \times dp[3]) = 6 dp[5]=max(1×4,1×dp[4],2×3,2×dp[3])=6

最终答案: d p [ 5 ] = 6 dp[5] = 6 dp[5]=6

1. 优化状态转移

当前的状态转移公式的时间复杂度为 O ( n 2 ) O(n^2) O(n2),因为每次计算 d p [ n ] dp[n] dp[n] 都需要枚举所有可能的拆分点 j j j。可以考虑利用数学性质来减少不必要的计算。

优化思路:

  • 减小计算量:拆分 n n n j j j n − j n-j nj 时,最大乘积通常出现在 j j j n − j n-j nj较为均匀的情况。因此,可以优先考虑接近 n 2 \frac{n}{2} 2n 的拆分点。

  • 剪枝:在实际实现中,可以通过观察某些 ( j ) 的拆分结果始终小于其他拆分的结果,从而剪去这些不必要的拆分点,加速计算。

2. 递归与迭代

动态规划可以通过递归或迭代来实现。递归形式易于理解,但迭代实现通常更高效,避免了函数调用的开销。

迭代实现的伪代码如下:

def integerBreak(n):dp = [0] * (n + 1)dp[1] = 1dp[2] = 1for i in range(3, n + 1):for j in range(1, i):dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]))return dp[n]

补充

公式 j × ( n − j ) j \times (n-j) j×(nj) 的由来,源于将整数 n n n 拆分成两部分时的直接乘积计算。它的推导过程如下:


1. 拆分的基本思想

整数拆分问题的核心是将一个整数 n n n 分成若干个整数的和:
n = a 1 + a 2 + ⋯ + a k n = a_1 + a_2 + \dots + a_k n=a1+a2++ak
为了最大化这些整数的乘积 P ( a 1 , a 2 , … , a k ) P(a_1, a_2, \dots, a_k) P(a1,a2,,ak),我们需要枚举可能的拆分方式。

一种最简单的拆分方式是只把 n n n 拆成两个整数 j j j n − j n-j nj,其中 j j j 是一个拆分点。此时,乘积为:
j × ( n − j ) j \times (n-j) j×(nj)

这直接反映了拆分点 j j j 的贡献和剩余部分 n − j n-j nj 的乘积。


2. 从数学推导来看

对于一个整数 n n n,如果只考虑拆分为两部分 j j j n − j n-j nj,它的乘积公式自然就是:
P = j × ( n − j ) P = j \times (n-j) P=j×(nj)
这里:

  • j j j 是第一个拆分出的部分。
  • n − j n-j nj 是剩下的部分。

这个公式来源于简单的代数计算:如果将 n n n 拆分为两部分,二者的乘积就是这些部分相乘。


3. 从动态规划的角度来看

动态规划的问题需要从小到大逐步计算。对于 d p [ n ] dp[n] dp[n],我们尝试枚举拆分点 j j j,将 n n n 分为两部分:

  1. 一部分是 j j j,它不会再拆分。
  2. 另一部分是 n − j n-j nj,它可以继续拆分。

直接拆分的乘积公式就是:
j × ( n − j ) j \times (n-j) j×(nj)
如果只考虑这一步拆分,这是计算的直接结果。


4. 为何需要直接乘积 j × ( n − j ) j \times (n-j) j×(nj)

在动态规划中,最大乘积有两种情况:

  1. 直接拆分的结果
    • 拆分为 j j j n − j n-j nj,只计算这两部分的乘积:
      P = j × ( n − j ) P = j \times (n-j) P=j×(nj)
    • 这是不进一步递归时的结果。
  2. 递归子问题的结果
    • 如果 n − j n-j nj 可以进一步拆分,我们取其最大乘积 d p [ n − j ] dp[n-j] dp[nj],然后乘以 j j j
      P = j × d p [ n − j ] P = j \times dp[n-j] P=j×dp[nj]
      两者都需要考虑,取其最大值。

5. 为什么需要枚举 j j j

在计算最大乘积时,拆分点 j j j 的选择对结果影响很大。例如:

  • j j j 较小时,剩余部分 n − j n-j nj 较大,可能需要进一步拆分。
  • j j j 较大时,剩余部分较小,可能直接求最大乘积。

通过枚举 j j j,我们可以覆盖所有可能的拆分情况,确保找到最大值。


6. 直观理解 j × ( n − j ) j \times (n-j) j×(nj) 的作用

  • j × ( n − j ) j \times (n-j) j×(nj)拆分后的直接乘积,体现了最简单的拆分方式。
  • 这是一种边界条件(base case),即当剩余部分 n − j n-j nj 不再进一步拆分时的结果。
  • 它提供了动态规划的基础值,与 j × d p [ n − j ] j \times dp[n-j] j×dp[nj] 共同构成状态转移的核心逻辑。

总结

公式 j × ( n − j ) j \times (n-j) j×(nj) 的灵感来自于拆分的基本操作,即将整数 n n n 分为两部分的直接乘积:

  1. 是最简单的整数拆分形式。
  2. 作为动态规划的一种情况,帮助递归计算中找到最大乘积。
  3. 构成状态转移公式的一部分,与递归解法( j × d p [ n − j ] j \times dp[n-j] j×dp[nj])相辅相成,确保覆盖所有可能的拆分情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/19904.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

OpenLayers教程11_在OpenLayers中启用WebGL渲染

在 OpenLayers 中启用 WebGL 渲染&#xff1a;提高地图渲染性能的完整指南 目录 一、引言二、WebGL 渲染在 Web GIS 中的作用 WebGL 的优势WebGL 与 Canvas 渲染的区别 三、在 OpenLayers 中启用 WebGL 的方法四、代码实现步骤 1. 初始化地图和基本 WebGL 渲染2. 加载大规模点…

利用Matlab函数实现深度学习算法

深度学习是一种机器学习技术&#xff0c;其核心是构建多层神经网络&#xff0c;通过深入的学习来实现对数据的有效建模和分析。在深度学习的发展过程中&#xff0c;产生了许多算法和框架&#xff0c;Matlab是其中之一&#xff0c;提供了大量的深度学习函数&#xff0c;可以帮助…

每日OJ题_牛客_dd爱旋转_模拟_C++_Java

目录 牛客_dd爱旋转_模拟 题目解析 C代码 Java代码 牛客_dd爱旋转_模拟 dd爱旋转 输入描述&#xff1a; 第一行一个数n(1≤n≤1000)&#xff0c;表示矩阵大小 接下来n行&#xff0c;每行n个数&#xff0c;描述矩阵&#xff0c;其中数字范围为[1,2000] 一下来一行一个数q(1…

从零开始打造个人博客:我的网页设计之旅

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 ✨特色专栏&#xff1a…

【C语言】操作符2(含操作符的应用)

1、单目操作符 单目操作符有下面几种&#xff1a; &#xff01;、、--、&&#xff08;取地址&#xff09;、*&#xff08;指针&#xff09;、&#xff08;正号&#xff09;、-&#xff08;负号&#xff09;、~、sizeof、&#xff08;类型&#xff09; 其中就还有&和*操…

博客文章怎么设计分类与标签

首发地址&#xff08;欢迎大家访问&#xff09;&#xff1a;博客文章怎么设计分类与标签 新网站基本上算是迁移完了&#xff0c;迁移之后在写文章的过程中&#xff0c;发现个人的文章分类和标签做的太混乱了&#xff0c;分类做的像标签&#xff0c;标签也不是特别的丰富&#x…

【计算机网络】物理层

&#x1f3af; 导读&#xff1a;本文档概述了计算机网络物理层的基础知识&#xff0c;包括物理层的作用、四大任务、传输媒体分类及其特性&#xff0c;深入讲解了调制技术和编码方法如曼彻斯特编码等&#xff0c;探讨了信道的极限容量&#xff0c;介绍了奈氏准则和香农公式&…

【AI赋能电商】数据分析和训练精准导向

AI赋能电商&#xff1a;重塑销售效率与用户体验的新篇章 一、AI驱动的购物推荐系统二、会员分类与精细化运营三、智能商品定价策略四、AI在供应链管理中的应用结语 在当今这个技术日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已不再是一个遥不可及的概念&…

多组织对接方案案例

前言 不同组织间的数据共享和整合&#xff0c;以便实现库存、订单的实时同步。多组织的对接需求往往一个销售订单需要再不同的组织生成不一样的单据&#xff0c;并且完成内部结算&#xff0c;这个案例对接的是金蝶云星空&#xff0c;具备多组织的特性&#xff0c;所以在前期规…

基于YOLOv8深度学习的医学影像肝脏肿瘤病症检测与诊断系统(PyQt5界面+数据集+训练代码)

随着医学影像技术和计算机视觉技术的快速发展&#xff0c;医疗诊断中的自动化工具正逐渐成为临床应用中的研究热点。在肝脏肿瘤的早期检测与诊断中&#xff0c;传统的人工方法耗时较长&#xff0c;且容易受医生的主观经验影响&#xff0c;诊断结果的准确性和一致性难以保证。基…

table元素纯css无限滚动,流畅过度

<template><div class"monitor-table-container"><table class"monitor-table"><thead><th>标题</th><th>标题</th><th>标题</th><th>标题</th></thead><tbody ref&quo…

springboot-事务失效以及排查过程

排查了好久&#xff0c;终于解决&#xff0c;希望这次的排查过程对大家也有帮助&#xff0c;废话少说&#xff0c;上源码 开发环境 springboot 2.3.11 jdk8 gradle6.4 HikariDataSource ps: 本环节使用双数据源&#xff0c;在service层做切面拦截&#xff0c;切换具体的数据源…

Docker入门之Windows安装Docker初体验

在之前我们认识了docker的容器&#xff0c;了解了docker的相关概念&#xff1a;镜像&#xff0c;容器&#xff0c;仓库&#xff1a;面试官让你介绍一下docker&#xff0c;别再说不知道了 之后又带大家动手体验了一下docker从零开始玩转 Docker&#xff1a;一站式入门指南&#…

信息与网络安全

1.对称密码体制的优缺点 优点&#xff1a;1.加密解密处理速度快 2.保密度高&#xff1b; 缺点&#xff1a;1.对称密码算法的密钥 分发过程复杂&#xff0c;所花代价高 2.多人通信时密钥组合的数量会出现爆炸性膨胀&#xff08;所需密钥量大&#xff09; 3.通信双方必须统一密钥…

GPT、Python和OpenCV支持下的空天地遥感数据识别与计算

在科技飞速发展的时代&#xff0c;遥感数据的精准分析已经成为推动各行业智能决策的关键工具。从无人机监测农田到卫星数据支持气候研究&#xff0c;空天地遥感数据正以前所未有的方式为科研和商业带来深刻变革。然而&#xff0c;对于许多专业人士而言&#xff0c;如何高效地处…

STM32完全学习——外部中断

一、嵌套向量中断控制器 我们在这里使用标准库的方式来处理。因此只需要调用几个函数就可以了。 NVIC_InitTypeDef NVIC_InitStruct; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //中断优先级分组 分1组NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); …

【动手做】安装Miniconda和jupyter notebook环境实现线性回归

Miniconda提供快速、简便的Python环境管理&#xff0c;包括安装、运行和更新软件包及其依赖项。Jupyter Notebook是一个交互式笔记本&#xff0c;在机器学习研究中广泛使用。本文旨在进行基础的环境配置&#xff0c;为后续的机器学习实践打好基础。 Miniconda与Jupyter Notebo…

7-简单巡检

KES的版本与license有效期 简单而又会产生灾难性的问题 使用version函数查看KES版本信息 test# select version();查看license有效期 test# select get_license_validdays(); 服务器的时区和时间 查看KES服务器的时区 test# show timezone; test# show time_zone; #两者皆…

【金融风控项目-07】:业务规则挖掘案例

文章目录 1.规则挖掘简介2 规则挖掘案例2.1 案例背景2.2 规则挖掘流程2.3 特征衍生2.4 训练决策树模型2.5 利用结果划分分组 1.规则挖掘简介 两种常见的风险规避手段&#xff1a; AI模型规则 如何使用规则进行风控 **使用一系列逻辑判断(以往从职人员的经验)**对客户群体进行区…

RabbitMQ高可用

生产者确认 生产者确认就是&#xff1a;发送消息的人&#xff0c;要确保消息发送给了消息队列&#xff0c;分别是确保到了交换机&#xff0c;确保到了消息队列这两步。 1、在发送消息服务的application.yml中添加配置 spring:rabbitmq:publisher-confirm-type: correlated …