热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

连续子数组最大和和最长递增子序列

本文内容框架:1连续子数组最大和基本方法、分治策略求解、动态规划求解2最长递增子序列排序LCS求解、动态规划、动态规划二分查找3小结1连续子数组最大和连续子数组最大和

本文内容框架:

§1 连续子数组最大和

基本方法、分治策略求解、动态规划求解

§2 最长递增子序列

排序+LCS求解、动态规划、动态规划+二分查找

§3 小结

 

§1 连续子数组最大和

 

 

 连续子数组最大和

连续子数组最大和,又叫最大子序列和或最大数组和,不过这里的序列好像有点不是很妥。

1.1问题描述

一个有N个元素的整型数组arr,有正有负,数组中连续一个或多个元素组成一个子数组,这个数组当然有很多子数组,求子数组之和的最大值。

 

1.2基本方法

枚举所有可能子数组,直接实现的时间复杂度是O(n³),代码如下:

 

int MaxSubsequenceSum( const int A[ ], int N ){int ThisSum, MaxSum, i, j, k;MaxSum = 0;for( i = 0; i MaxSum )MaxSum = ThisSum;}return MaxSum;}

 

  做一点小优化:没有必要每次都去从头计算ThisSum,可以之间在前面的基础上加上增加的那一个,时间复杂度为O(n²),还看代码:

1.3分治策略求解

分治策略的时间复杂度是O(NlogN)。分治策略在这里是从中间向两边延伸最大子数组求最大和。

 

static int Max3( int A, int B, int C ){return A > B ? A > C ? A : C : B > C ? B : C;}static int MaxSubSum( const int A[ ], int Left, int Right ){int MaxLeftSum, MaxRightSum;int MaxLeftBorderSum, MaxRightBorderSum;int LeftBorderSum, RightBorderSum;int Center, i;if( Left &#61;&#61; Right ) /* Base case */if( A[ Left ] > 0 )return A[ Left ];elsereturn 0;Center &#61; ( Left &#43; Right ) / 2;MaxLeftSum &#61; MaxSubSum( A, Left, Center );MaxRightSum &#61; MaxSubSum( A, Center &#43; 1, Right );MaxLeftBorderSum &#61; 0; LeftBorderSum &#61; 0;for( i &#61; Center; i >&#61; Left; i-- ){LeftBorderSum &#43;&#61; A[ i ];if( LeftBorderSum > MaxLeftBorderSum )MaxLeftBorderSum &#61; LeftBorderSum;}MaxRightBorderSum &#61; 0; RightBorderSum &#61; 0;for( i &#61; Center &#43; 1; i <&#61; Right; i&#43;&#43; ){RightBorderSum &#43;&#61; A[ i ];if( RightBorderSum > MaxRightBorderSum )MaxRightBorderSum &#61; RightBorderSum;}return Max3( MaxLeftSum, MaxRightSum,MaxLeftBorderSum &#43; MaxRightBorderSum );}int MaxSubsequenceSum( const int A[ ], int N ){return MaxSubSum( A, 0, N - 1 );}

 

1.4动态规划求解

 

设b[i]表示以a[i]结尾 的子数组的最大子段和&#xff0c;即&#xff1a;b[i]&#61;max{sum(a[j~k])},其中0<&#61;j<&#61;i&#xff0c;j<&#61;k<&#61;i。因此对于数组a[0~n]的最大字段和为max{b[i]}&#xff0c;其中0<&#61;i

 

在计算b[i]时&#xff0c;可以考虑以下三种情况&#xff1a;

1&#xff0c;b[i] &#61; b[i-1]&#43;a[i]&#xff0c;当b[i-1]>0时&#xff0c;这时候的b[i]中包含a[i]。

2&#xff0c;b[i] &#61; a[i]&#xff0c;当b[i-1]<&#61;0&#xff0c;这时候以a[i]重新作为b[i]的起点。

3&#xff0c;b[i]不包含a[i]的情况&#xff0c;这种情况在计算b[i]之前已经计算处结果&#xff0c;保存在b[0~i-1]中。最后计算max{b[i]}时会考虑到。

b[i] &#61; max{ b[i-1]&#43;a[i]&#xff0c;a[i]}。

而数组a[0~n]则为max{b[i]}。在实现时&#xff0c;可以省略数组b[i]。

╝②

动态规划求解的时间复杂度是O(n)。可以记录最大连续子数组和的起始和末尾位置。

 

 

int MaxSubsequenceSum( const int A[ ], int N ){int ThisSum, MaxSum, j;ThisSum &#61; MaxSum &#61; 0;for( j &#61; 0; j MaxSum )MaxSum &#61; ThisSum;else if( ThisSum <0 )ThisSum &#61; 0;}return MaxSum;}

 ╝①

 

1.5问题拓展——最大子矩阵和

对于最大子矩阵和&#xff0c;当然可以使用枚举方法&#xff0c;但是这显然不是作者想要的&#xff0c;一维的情况已经得到很好的解法&#xff0c;要是能将二维情况转换为一维的情况&#xff0c;效率就容易接受了。

最大子矩阵和的行也是连续的&#xff0c;计算第 i 行和第 j 行之间的最大子矩阵&#xff0c;可以将 i 行和 i 行之间的同一列的元素纵向相加&#xff0c;这样子矩阵就转换为一维的数组&#xff0c;就是下图所示&#xff1a;


固定第i列到第j列的范围&#xff0c;寻找在这个范围内的最大子矩阵&#xff0c;这个寻找过程&#xff0c;把每行第i列上的元素到第j列的元素分别求和&#xff0c;就转变为了一维的情况。由于有C(n,2)种列的组合&#xff0c;而求一维的子序列需要n的时间&#xff0c;所以&#xff0c;总体上时间复杂度为O(n^3)。

 仍旧贴代码&#xff1a;

 

#include
using namespace std;
#define N 4
int main() {int A[N][N] &#61; { 0, -2, -7, 0, 9, 2, -6, 2, -4, 1, -4, 1, -1, 8, 0, -2 }; int F[N&#43;1][N&#43;1];int P[N&#43;1];int Q[N&#43;1];int max, index_i, index_j;// F for(int i&#61;1; i0?(P[k-1]&#43;F[k][j]-F[k][i-1]):(F[k][j]-F[k][i-1]);Q[k] &#61; Q[k-1]>P[k]?Q[k-1]:P[k]; }if(Q[N] > max) {max &#61; Q[N];index_i &#61; i;index_j &#61; j;}}}// maxcout <}

╝③ 

 

§2 最长递增子序列

 

最长递增子序列

2.1问题描述

L&#61;<a1,a2,…,an>n个不同的实数的序列&#xff0c;L的递增子序列是这样一个子序列Lin&#61;<aK1,ak2,…,akm>&#xff0c;其中k12<…maK1k2<…km。求最大的m值。

2.2动态规划求解

从后向前分析&#xff0c;如果a[i]大于前面所有的数&#xff0c;则dp[i]在max{dp[j],j

设dp(i)表示L中以ai为末元素的最长递增子序列的长度。则有如下的递推方程&#xff1a;

 

这个递推方程的意思是&#xff0c;在求以ai为末元素的最长递增子序列时&#xff0c;找到所有序号在L前面且小于ai的元素aj&#xff0c;即jaji。如果这样的元素存在&#xff0c;那么对所有aj,都有一个以aj为末元素的最长递增子序列的长度dp(j)&#xff0c;把其中最大的dp(j)选出来&#xff0c;那么f(i)就等于最大的f(j)加上1&#xff0c;即ai为末元素的最长递增子序列&#xff0c;等于以使f(j)最大的那个aj为末元素的递增子序列最末再加上ai&#xff1b;如果这样的元素不存在&#xff0c;那么ai自身构成一个长度为1的以ai为末元素的递增子序列。

╝④

 

 

#include
using namespace std;/* 最长递增子序列 LIS* 设数组长度不超过 30* DP
*/int dp[31]; /* dp[i]记录到[0,i]数组的LIS */
int lis; /* LIS 长度 */int LIS(int * arr, int size)
{for(int i &#61; 0; i arr[j] && dp[i] lis){lis &#61; dp[i];}}}}return lis;
}/* 输出LIS */
void outputLIS(int * arr, int index)
{bool isLIS &#61; 0;if(index <0 || lis &#61;&#61; 0){return;}if(dp[index] &#61;&#61; lis){--lis;isLIS &#61; 1;}outputLIS(arr,--index);if(isLIS){printf("%d ",arr[index&#43;1]);}
}void main()
{int arr[] &#61; {1,-1,2,-3,4,-5,6,-7};/* 输出LIS长度&#xff1b; sizeof 计算数组长度 */printf("%d\n",LIS(arr,sizeof(arr)/sizeof(int)));/* 输出LIS */outputLIS(arr,sizeof(arr)/sizeof(int) - 1);printf("\n");
}

 

2.3排序&#43;LCS求解

这个方法也是很直观的&#xff0c;对原数组a进行排序得到一个有序的数组b&#xff0c; 这样出现在数组a的最长递增子序列也一定是数组b的子序列。

 

#include
using namespace std;/* 最长递增子序列 LIS* 设数组长度不超过 30* quicksort &#43; LCS
*/void swap(int * arr, int i, int j)
{int tmp &#61; arr[i];arr[i] &#61; arr[j];arr[j] &#61; tmp;
}void qsort(int * arr, int left, int right)
{if(left >&#61; right) return ;int index &#61; left;for(int i &#61; left&#43;1; i <&#61; right; &#43;&#43;i){if(arr[i] }int dp[31][31];int LCS(int * arr, int * arrcopy, int len)
{for(int i &#61; 1; i <&#61; len; &#43;&#43;i){for(int j &#61; 1; j <&#61; len; &#43;&#43;j){if(arr[i-1] &#61;&#61; arrcopy[j-1]){dp[i][j] &#61; dp[i-1][j-1] &#43; 1;}else if(dp[i-1][j] > dp[i][j-1]){dp[i][j] &#61; dp[i-1][j];}else{dp[i][j] &#61; dp[i][j-1];}}}return dp[len][len];
}void main()
{int arr[] &#61; {1,-1,2,-3,4,-5,6,-7};int arrcopy [sizeof(arr)/sizeof(int)];memcpy(arrcopy,arr,sizeof(arr));qsort(arrcopy,0,sizeof(arr)/sizeof(int)-1);/* 计算LCS&#xff0c;即LIS长度 */int len &#61; sizeof(arr)/sizeof(int);printf("%d\n",LCS(arr,arrcopy,len));
}

 

2.4动态规划&#43;二分查找

如果对前面的方法的时间复杂度&#xff08;前两种复杂度是O(n²)还不满意的&#xff0c;这里就有救了&#xff0c;动态规划&#43;二分查找的实现时间复杂度是O(NlogN)。 

 

在前i个元素中的所有长度为len的递增子序列中找到这样一个序列&#xff0c;它的最大元素比arr[i&#43;1]小&#xff0c;而且长度要尽量的长&#xff0c;如此&#xff0c;只需记录len长度的递增子序列中最大元素的最小值就能使得将来的递增子序列尽量地长。

方法&#xff1a;维护一个数组B[i]&#xff0c;记录长度为i的递增子序列中最大元素的最小值&#xff0c;并对于数组中的每个元素考察其是哪个子序列的最大元素&#xff0c;二分更新B数组&#xff0c;最终i的值便是最长递增子序列的长度。这个方法真是太巧妙了&#xff0c;妙不可言。

╝⑥

 

 

int LIS(int d[], int n){int *B &#61; new int[n];int left, right, mid, len &#61; 1;B[0] &#61; d[1]; //为了和上面的一致&#xff0c;我们从1开始计数吧:)for(i &#61; 2; i <&#61; n; &#43;&#43;i){left &#61; 0, right &#61; len;while(left <&#61; right){mid &#61; (left &#43; right) / 2;if(B[mid] len) len&#43;&#43;; //d[i]比现有的所有数字都大&#xff0c;所以left 才会大于 len。}delete[] B;return len;
}

 ╝⑤

 

 

 

 

§3 小结

 

 

这篇博文介绍了最大连续子数组和最长递增子序列的求解&#xff0c;两个问题都介绍了数种方法&#xff0c;希望能彻底理解问题的本质以及求解方法。如果你有任何建议或者批评和补充&#xff0c;请不吝留言指出&#xff0c;不胜感激&#xff0c;更多参考请移步互联网。

 

 

 

 

 

参考&#xff1a;

①shengjin&#xff1a; http://www.cnblogs.com/shengjin/archive/2010/01/08/1641975.html

②clearriver&#xff1a; http://blog.csdn.net/clearriver/article/details/4224154

③xiaodongrush&#xff1a; http://www.cnblogs.com/pangxiaodong/archive/2011/09/02/2163445.html

④算法驿站&#xff1a; http://blog.pfan.cn/rickone/13086.html

felix021&#xff1a; http://www.felix021.com/blog/read.php?1587

勇幸|Thinking&#xff1a; http://www.ahathinking.com/archives/117.html

 

 


推荐阅读
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 判断数组是否全为0_连续子数组的最大和的解题思路及代码方法一_动态规划
    本文介绍了判断数组是否全为0以及求解连续子数组的最大和的解题思路及代码方法一,即动态规划。通过动态规划的方法,可以找出连续子数组的最大和,具体思路是尽量选择正数的部分,遇到负数则不选择进去,遇到正数则保留并继续考察。本文给出了状态定义和状态转移方程,并提供了具体的代码实现。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
author-avatar
翔_至死不渝
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有