最大子段-n上找m个子段的和为最大-动态规划-二维dp+滚动数组dp优化
来源:互联网 发布:pdf.js 打开 word文档 编辑:程序博客网 时间:2024/06/18 12:22
1.二维dp
dp[i][j]代表的是j长度上找到i段,使得i段和最大。(其中最后一段的最后一位一定要是a[j],这句话不理解的可以看看http://blog.csdn.net/qq_36523667/article/details/78598426)
这时最后一段分为两种情况:
1.a[j]是最后一段的一部分
2.a[j]自己成为最后一段
用算式表示为
1.dp[i][j - 1] + a[j]意思就是从j-1中重新选i段,再把j添加到最后一段的最后
2.max(dp[i - 1][k]) + a[j],i - 1 <= k < j,意思就是从i-1到j中重新选i段取最大值,再加上最后一段组成i段
代码
// TODO: 2017/11/22 动态规划m子段-二维dpint dynamicM(int[] a, int m) { int n = a.length; int[][] dp = new int[100][100]; for (int i = 1; i <= m; i++) { for (int j = i; j < n; j++) { int max = 0; //在当前j找?段的最优解(?是因为找几段并没有体现出来) for (int k = i - 1; k < j; k++) { max = Math.max(max, dp[i - 1][k]); } dp[i][j] = Math.max(dp[i][j - 1] + a[j], max + a[j]); } } return dp[m][n - 1];}
2.滚动数组优化
由于上面的时间复杂度到达了n^3,所以不优化会爆掉的。
仔细看一下上面的代码,虽然思考模式是j中找i个,但是实际上i根本就没有用到,所以我们先把二维dp变成一维的。
此外
for (int k = i - 1; k < j; k++) {
max = Math.max(max, dp[i - 1][k]);
}
只需要记录下这个j位上的最大值即可。下一次循环的时候,再去使用记录下来的最大值。
// TODO: 2017/11/22 动态规划m子段-滚动数组int dynamicM2(int[] a, int m) { int n = a.length; int[] max = new int[100]; int[] dp = new int[100]; int mMax = Integer.MIN_VALUE; for (int i = 1; i <= m; i++) { mMax = Integer.MIN_VALUE; for (int j = i; j < n; j++) { dp[j] = Math.max(dp[j - 1] + a[j], max[j - 1] + a[j]); max[j - 1] = mMax; mMax = Math.max(mMax, dp[j]); } } return mMax;}
综上所述,第一份代码写出来,和二维dp的思维有关,是有迹可循的。但是第二份代码写出来,完全是因为,发现了,第一份代码虽然思维上是找在不同段上找不同i的最优解,但是i的作用没有体现出来,所以可以察觉到可以把二维降成一维,并且记录下max就可以消除一个循环的复杂度。第二份代码与技术有关。
当然了,第二份代码想直接写出来,需要另一种思维。
第二份代码到底是怎样的一种思维?
(前方高能,灵魂升华!)
先重新讲一下有关于这一题的dp:
n中找m段使之和最大,最后一段的最后一位有可能在各个地方,可能正好就是在n上,也可能是n-1上。。。
所以这里就把状态抽象出来了:j上找i个使之和最大,但是多了一个条件,a[j]必须在最后一段的最后一位上。
所以我们的j是可以在不同的位置上
for (int j = i; j < n; j++) {以此实现了遍历每一种情况。
所以我们的思维就是这样的,在不同的j上找m段使之最大。这些不同的结果我们取一个最大值就是我们的结果了。
实在不懂我来个图。
你最后一段的最后一个位置可以在任何一个地方。所以我们的j循环就是针对这个而设置的。那么你的dp[j]就代表了最后一段的最后一个数在所有不同位置上的找m段使之和最大的情况。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
好吧,我前面还是讲的太乱了。最后简单浓缩的讲一下。
还是看这个图。你的第一段的第一个数,和你最后一段的最后一个数,有好多好多个可能。我们用
for (int i = 1; i <= m; i++) { for (int j = i; j < n; j++) {这两个for循环的目的就是把你的第一个点(后面称之为左节点)和你最后一个点(后面称之为右节点)的所有情况都列举出来(这里i=1是因为我们传进来的数组是从1开始赋值的),然后,再在这一段区间中去找m段使之和最大。(这就是下面这个公式中dp[j]的含义)
dp[j] = Math.max(dp[j - 1] + a[j], max[j - 1] + a[j]);所以这个公式的可以这样理解:
如果你的a[j]是最后一段的一部分,你就在你的左节点相同,右节点-1的这种情况找到m个,a[j]添加到最后一段中去。
如果你的a[j]自成一段,你就需要在你的左节点相同,右节点范围左节点到原右节点之间找到m-1个。
max[j - 1] = mMax;mMax = Math.max(mMax, dp[j]);
mMax就是记录了你当前位之前最大的dp
这题让我领悟到了:最对的思维+最强的技巧=最优的性能
- 最大子段-n上找m个子段的和为最大-动态规划-二维dp+滚动数组dp优化
- 17089 最大m子段和(scauoj、dp动态规划)
- HDU1024 DP的优化 最大M子段和问题
- 环形m段最大子段和 题解动态规划DP
- HDU 1024 最大M子段和 滚动数组优化
- 【动态规划】最大m子段和
- HDU 1024 最大m个子段和滚动数组
- DP最大M子段和
- DP动态规划--最大子段和--Max Sum
- 51nod 最大子段和(动态规划DP)
- 动态规划问题-DP 最大子段和O(n)解决方法
- DP-循环数组最大子段和
- 循环数组最大子段和 (dp)
- 动态规划---最大子段和,最大子矩阵和,最大m子段和
- NYOJ 746 - 正整数n划分为m段,求m段的最大乘积 【区间DP】
- hdu 1024 DP 求n个数m个不交叉子段的最大和
- HDU1081二维子数组最大和/压缩/动态规划/DP
- dp求最大子段(一维数组,二维数组)
- 【Scikit-Learn 中文文档】随机梯度下降
- 欢迎使用CSDN-markdown编辑器
- div 层基础知识
- RMQ模板题 poj3264 Balanced Lineup
- 剑指offer:数组中重复的数字
- 最大子段-n上找m个子段的和为最大-动态规划-二维dp+滚动数组dp优化
- 彻底剖析numpy的数值运算
- 模拟好题 CodeForces 864CBus
- C++文件操作与文件流学习
- 笔记
- 【Scikit-Learn 中文文档】最近邻
- <Java算法实现--LeetCode(4)(7)>2017-11-22
- Linux之Redhat中 Web端建立mysql数据库及论坛
- 一维字符串hash模板