一维数组的连续子数组的最大和
题目:输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
这是一个典型的DP问题,递推公式是DP(i) = max{DP(i-1) + A(i),A(i)}
解释:DP(i)代表末尾元素为A(i)的连续子数组的最大和。这个地推公式来自于一个显而易见的事实:在遍历数组的开始,维护当前遍历过的数的和的一个记录,如果这个记录已经小于0且是第一个小于0的时候,那么在处理下一个数的时候,该记录加上新的数的值一定小于该数,所以保存的记录已经无作用了,也就是说新的记录的起点应该和旧记录的起点不一样。那么新的记录应该是什么数呢?也就是说新纪录的起点该选择从哪开始呢?注意到此次处理的前一个数一定是一个负数(由于这是我们考虑的第一个小于0的记录),同时一个记录的起点一定是正数,所以新的起点只可能是在旧的起点的下一个位置和此次处理的前一个数的前一个数之间,假设存在该新起点,那么从该起点到旧记录终点的和应该大于零,而旧记录的起点到终点的和是小于0的,也就是说要使旧记录的起点到该新的起点的和小于0,这与旧记录的终点是第一个小于0的记录所矛盾,所以新的起点在之前的数组中不存在,因此新纪录需重新赋值为0,这也是递推公式的含义。
二维数组的连续子数组的最大和
二维数组(m,n)的连续子数组即为一个矩阵,如下图所示
设矩阵的坐上顶点为A(i,j), 右下顶点为 B(endi, endj). 在图中即为 A(1,1), B(3,3)。我们可以用Rect(A,B)即 (1,1,3,3,)来表示矩形区域。
解法一:遍历所有子矩阵,维护其中和最大的值。同时在求子矩阵喝的时候可以利用递推公式降低复杂度。时间复杂度是O(m^2n^2)
解法二:
基于一维动归来求解。由于子矩阵和中得行都是连续的,因此可以先遍历矩阵的连续行,并将它们对应列元素求和,保存到一维数组,这样就转化到了一维问题,时间复杂度是O(m^2n),代码如下:
#include <cstdio>
#include <cstdlib>
#define MAX_N 100
int max(int x,int y){
return x>y?x:y;
}
int main()
{
int n;
int input[MAX_N][MAX_N] = {0};
int cloumnSum[MAX_N+1][MAX_N+1] = {0};
scanf("%d",&n);
int count = 0,temp;
while(scanf("%d",&temp)!= EOF){
int row = count/n;
int column = count%n;
input[row][column] = temp;
cloumnSum[row+1][column] = cloumnSum[row][column] + temp;
count++;
if(count >= n*n) break;
}
int sum = 0,maxNum = 0;
for(int i = 0;i<n;i++){
for(int j = i;j<n;j++){
sum = 0;
for(int s = 0;s< n;s++){
sum += cloumnSum[j+1][s] - cloumnSum[i][s];
if(sum < 0) sum = 0;
else if(sum>maxNum) maxNum = sum;
}
}
}
printf("%d",maxNum);
return 0;
}
后记:虽然老师要求DP问题要写清楚初始条件、转移方程、转移条件、时间复杂度、空间复杂度、反向追踪等等,可惜有点懒呀,很多不想写了,这也不是考试⊙0⊙