整数拆分

力扣题目链接(opens new window)

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

示例 1:

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

示例 2:

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

思路

题目的要求很明确了,就是把整数拆解,让拆分结果相乘,积尽可能大

一个数学结论是:一个整数,如果要拆分之后再相乘得到尽可能大的值,那么拆分时就要拆成尽可能相同的m个数

上述结论在示例2中可以体现

对 10 进行拆分有很多种可能,但乘积相对大的几种可能都集中在拆分之后的数尽可能相同的几种情况中,例如:

5*5
3*3*4

取其中最大的情况作为结果即可(3 * 3 * 4)

再来看一下拆分的过程

还是以 10 为例:

10--->2  8
_--->2 6
_--->2 4
...

10 可以拆成 2 和 8;

然后 8 又进一步拆成 2 和 6;

以此类推,我们发现这里出现了一种递推的关系:

10 依靠 2 和 8 的状态;

8 依靠 2 和 6 的状态;...

因此可以使用dp

五步走

1、确定dp数组含义

给我们的数是 i ,要对 i 进行拆分,所以

dp[i]: 对i进行拆分得到的最大乘积为dp[i]

2、确定递推公式

怎么拆?有两种渠道得到dp[i]:

  • 一个是j * (i - j) 直接相乘
  • 一个是j * dp[i - j],相当于是拆分(i - j)

j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘。

假设现在需要拆分的数是 i ,并且需要拆成两个数

那就可以表示如下:

j*(i-j)//j和(i-j)分别是拆分后得到的数

j表示遍历从[1,i]得到的所有情况

如果要拆成两个数或者以上呢?那就要表示如下:

j*dp[i-j]//j和(i-j)分别还是拆分后得到的数

不同的是,我们要对 i-j 进行进一步拆分,得到三个或三个以上的数

需要注意的是:上述拆分方式中,j 是固定的,在拆分 i-j 的过程中就会包括拆分j的所有情况,因此不用再对 j 进行拆分

(联想一下背九九乘法表的场景,横着背和竖着背最后都能得到所有情况)

如果要写成dp[j]*dp[i-j]就有点问题了

​ 第一,这么写表示 i 一定要继续拆分,这样可能就错过最大乘积了,相当于默认将一个数强制拆成4份以及4份以上了;(例如本来3 * 3 * 4最大了,拆i就拆成1 * 2 * 3 * 4,就不一定是最大了)

​ 第二,上述写法在初始化时不符合dp数组的含义;

总结一下

j是从1开始遍历,拆分j的情况,在遍历j的过程中其实都计算过了。那么从1遍历j,比较(i - j) * jdp[i - j] * j 取最大的。

递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));

(为什么还要加dp[i],后面在代码里解释)

3、确定初始化方法

首先,dp[0]和dp[1]其实是没有意义的,0、1拆不了

然后dp[2]是有意义的,为1,所以:

dp[0] = 0//0*0不影响
dp[1] = 0
dp[2] = 1

4、确定遍历顺序

先看递推公式:dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));

dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历,先有dp[i - j]再有dp[i]。

所以遍历方式如下:

for(int i = 3; i <= n; ++i){
for(int j = 1; j < i - i; ++j){//从0开始没有意义
dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j)));
}
}

递推公式中取最大值时加入dp[i]的原因是,在第二层循环中,我们也要找到最大的dp[i],因为题意就是获取乘积最大的拆分结果(也就是dp[i])嘛

代码

本题代码很简单,就是分析过程比较难(纯数学题)

class Solution {
public:
int integerBreak(int n) {
//定义dp数组
vector<int> dp(n + 1);
//初始化dp数组
dp[0] = 0;
dp[1] = 0;
dp[2] = 1;
//遍历
for(int i = 3; i <= n; ++i){
for(int j = 1; j < i; ++j){//注意这里的循环条件,遍历范围是i
dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j]));
}
}
return dp[n];//注意返回值是dp[n],不用减1
}
};

需要注意循环的条件和返回值

【LeetCode动态规划#03】整数拆分(数学题)的更多相关文章

  1. [LeetCode] Integer Break 整数拆分

    Given a positive integer n, break it into the sum of at least two positive integers and maximize the ...

  2. LeetCode 343.整数拆分 - JavaScript

    题目描述:给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 题目分析 题目中"n 至少可以拆分为两个正整数的和",这个条件说 ...

  3. Java实现 LeetCode 343 整数拆分(动态规划入门经典)

    343. 整数拆分 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × ...

  4. LeetCode 343. 整数拆分(Integer Break) 25

    343. 整数拆分 343. Integer Break 题目描述 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 每日一算法2019/5/2 ...

  5. HDU 4651 Partition(整数拆分)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4651 题意:给出n.求其整数拆分的方案数. i64 f[N]; void init(){    f[0 ...

  6. LightOJ 1336 Sigma Function(数论 整数拆分推论)

    --->题意:给一个函数的定义,F(n)代表n的所有约数之和,并且给出了整数拆分公式以及F(n)的计算方法,对于一个给出的N让我们求1 - N之间有多少个数满足F(x)为偶数的情况,输出这个数. ...

  7. LightOJ 1341 Aladdin and the Flying Carpet(整数拆分定理)

    分析:题目并不难理解,就是一些细节上的优化需要我们注意,我在没有优化前跑了2000多MS,优化了一些细节后就是400多MS了,之前还TLE了好几次. 方法:将整数拆分为质因子以后,表达为这样的形式,e ...

  8. HDU1028 (整数拆分)

    Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K ...

  9. 整数拆分问题_C++

    一.问题背景  整数拆分,指把一个整数分解成若干个整数的和 如 3=2+1=1+1+1  共2种拆分 我们认为2+1与1+2为同一种拆分 二.定义 在整数n的拆分中,最大的拆分数为m,我们记它的方案数 ...

  10. Pollard-Rho大整数拆分模板

    随机拆分,简直机智. 关于过程可以看http://wenku.baidu.com/link?url=JPlP8watmyGVDdjgiLpcytC0lazh4Leg3s53WIx1_Pp_Y6DJTC ...

随机推荐

  1. [转帖]配置logback上报日志到Skywalking

    https://zhuanlan.zhihu.com/p/506119895 配置logback上报日志到Skywalking 配置logback上报日志到skywalking需要引入toolkit依 ...

  2. [转帖]台积电3nm工艺细节曝光

    https://weibo.com/ttarticle/p/show?id=2309404853901739557561&sudaref=www.baidu.com ​​2023年1月3日消息 ...

  3. Harbor简单搭建以及异常排查的过程与思路

    Harbor简单搭建以及异常排查的过程与思路 前言 我发现我总是能够遇到别人遇不到的问题. 本来搭建十分钟就可以搭建完成 结果我硬生生的搭建了四十分钟. 为了保证下次不再浪费时间. 这里加单总结一下遇 ...

  4. postman数据驱动(.csv文件)

    做api测试的时候同一个接口我们会用大量的数据(正常流/异常流)去验证,要是一种场 景写一个接口的话相对于比较麻烦,这个时候就可以使用数据驱动来实现 1.本地创建一个txt文件,第一行写上字段名,多个 ...

  5. Spring Boot Admin极简教程

    一.简介 Spring Boot Admin是一个完整的应用程序,用于监控和管理Spring Boot应用.原理是通过调用Spring Boot Actuator提供的http接口来实现的监控和管理, ...

  6. 【JS 逆向百例】房天下登录接口参数逆向

    声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:房天下账号密码登录 主页:https://passpo ...

  7. Go 跟踪函数调用链,理解代码更直观

    Go 跟踪函数调用链,理解代码更直观 目录 Go 跟踪函数调用链,理解代码更直观 一.引入 二.自动获取所跟踪函数的函数名 三.增加 Goroutine 标识 四.让输出的跟踪信息更具层次感 五.利用 ...

  8. 8.4 Windows驱动开发:文件微过滤驱动入门

    MiniFilter 微过滤驱动是相对于SFilter传统过滤驱动而言的,传统文件过滤驱动相对来说较为复杂,且接口不清晰并不符合快速开发的需求,为了解决复杂的开发问题,微过滤驱动就此诞生,微过滤驱动在 ...

  9. 1.1 Windows驱动开发:配置驱动开发环境

    在进行驱动开发之前,您需要先安装适当的开发环境和工具.首先,您需要安装Windows驱动开发工具包(WDK),这是一组驱动开发所需的工具.库.示例和文档.然后,您需要安装Visual Studio开发 ...

  10. IDEA破解(无限重启激活时间版)

    下载地址[将下载的目录打成zip压缩包后使用]:「ide-eval-resetter」https://www.aliyundrive.com/s/UFHpDX5d6Xv 点击链接保存,或者复制本段内容 ...