整数拆分

力扣题目链接(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. [转帖]QPS、TPS、RT、并发数、吞吐量理解和性能优化深入思考

    https://baijiahao.baidu.com/s?id=1675704570461446033&wfr=spider&for=pc 吞吐量 在了解qps.tps.rt.并发数 ...

  2. Linux查看登录用户记录信息

    Linux查看登录用户记录信息 登录成功的信息 last 可以简单统计一下: last |awk '{print $3}' |sort |uniq -c |sort -k1nr 登录失败的 就是 la ...

  3. 你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队

    1.前言 不要犹豫了,GC最大停顿时间小于1ms,支持16TB内存,这么高的性能提升,也不需要复杂的调优,节省了这个时间,你去陪对象不香嘛. 上篇文章给大家带来了JDK11升级JDK17的最全实践,相 ...

  4. 【JS 逆向百例】网洛者反爬练习平台第二题:JJEncode 加密

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  5. endnote文献使用简明教程+遇到问题

    安装下载endnote 1.双击[EndNote X9 v19.0.0.12062 Setup.msi]安装EndNote X9,安装时选择试用,安装完成后不要运行EndNote: 2.如果想使用汉化 ...

  6. C# 语言程序设计笔记

    C#是一种最新的.面向对象的编程语言.它使得程序员可以快速地编写各种基于Microsoft .NET平台的应用程序,Microsoft .NET提供了一系列的工具和服务来最大程度地开发利用计算与通讯领 ...

  7. 使用 docker 部署 kafka

    在很多时候我们需要使用到消息队列, 其中 kafka 是一个非常优秀的消息队列, 在我们平时开发中也经常会用到, 但是在开发环境中部署 kafka 是一个非常麻烦的事情 在 kafka 官网上, 有一 ...

  8. Nginx负载均衡、location匹配

    nginx的日志 ``` #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$ ...

  9. OpenGL的深度缓冲

      如果我们想要在三维空间里画两个正方形:一个红色的,一个绿色的,而且从人眼的观察角度看,绿色正方形在红色正方形的后面,最后看上去应该是这样的: 要点在于,从观察者的角度看,绿色正方形在红色正方形的后 ...

  10. Python 潮流周刊第 38 期(摘要)+赠书5本

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...