连续子数组的最大和
在线编程地址之一https://www.nowcoder.com/question/next?pid=17095741&qid=501563&tid=26279803
题目描述:给定一个数组arr,数组中的元素有整数也有负数,数组中的一个或者连续多个数组成一个子数组。
求所有子数组里面的最大和。例如现在有数组 {1 , -2 , 3 , 10 , -4 , 7 , 2 , -5 }。
输入:[1, -2, 3, 10, -4, 7, 2, -5]
输出:18
注意:测试用例注意全为负数的情况
解法1:暴力求解,三层循环,sum=array[i...j]其中的任意连续子数组和
public int FindGreatestSumOfSubArray(int[] array) {
int len=array.length;
if(len<1)return 0;
if(len==1)return array[0];
int res=array[0];
for(int i=0;i<len;i++){
for(int j=i;j<len;j++){
int curSum=0;
for(int k=i;k<=j;k++){
curSum+=array[k];
}
res=Math.max(res,curSum);
}
}
return res;
}
解法1-1,暴力的改进,使得为O(n*n),
x[i..j]之和 = x[i..j-1]之和 + x[j]
public int FindGreatestSumOfSubArray(int[] array) {
int len=array.length;
if(len<1)return 0;
if(len==1)return array[0];
int res=array[0];
for(int i=0;i<len;i++){
int curSum=0;
for(int j=i;j<len;j++){
curSum+=array[j];
res=Math.max(res,curSum);
}
}
return res;
}
解法2:分支法O(nlogn),每次从数组的的中间节点,求以此点为核心,向左右扩展,得到最大和,然后再比较最大和在左边,在右边这两种情况比较
伪代码:maxSum3(0,arr.length-1)
float maxsum3(l, u)
if (l > u) /* zero elements */
return 0
if (l == u) /* one element */
return max(0, x[l])
m = (l + u) / 2
/* find max crossing to left */
lmax = sum = 0
for (i = m; i >= l; i--)
sum += x[i]
lmax = max(lmax, sum)
/* find max crossing to right */
rmax = sum = 0
for i = (m, u]
sum += x[i]
rmax = max(rmax, sum)
return max(lmax+rmax,
maxsum3(l, m),
maxsum3(m+1, u));
解法3:动态规划
dp[i]记录当前以i结尾的连续数组的最大值
(1)如果 dp[i]= dp[i-1]+nums[i] 比 num[i] 则dp[i] 小,就不是连续数组的最大值,另起炉灶,dp[i]=nums[i],状态从【i】重新开始,并把这个状态记录下来
(2)如果 dp[i]= dp[i-1]+nums[i] 比 num[i] 则dp[i] 小,是连续数组的最大值,把这个状态记录下来
(3)考虑特殊情况,比如数组为空或者为1
public int FindGreatestSumOfSubArray(int[] array) {
int len=array.length;
if(len<1)return 0;
if(len==1)return array[0];
int dp[]=new int[len];
dp[0]=array[0];
int res=dp[0];
for(int i=1;i<len;i++){
dp[i]=Math.max(dp[i-1]+array[i],array[i]);
res=Math.max(res,dp[i]);
}
return res;
}
//不用数组,空间优化
public int FindGreatestSumOfSubArray(int[] array) {
int res = array[0]; //记录当前所有子数组的和的最大值
int max=array[0]; //包含array[i]的连续数组最大值
for (int i = 1; i < array.length; i++) {
max=Math.max(max+array[i], array[i]);
res=Math.max(max, res);
}
return res;
}
//另外一种写法
public int FindGreatestSumOfSubArray(int[] array) {
int len=array.length;
if(len<1)return 0;
if(len==1)return array[0];
int res=array[0];
int presum=res;
for(int i=1;i<len;i++){
if(presum<0){
presum=array[i];
}
else{
presum=Math.max(presum+array[i],array[i]);
}
res=Math.max(res,presum);
}
return res;
}
最大子矩阵的和
在线编程https://www.nowcoder.com/questionTerminal/a5a0b05f0505406ca837a3a76a5419b3
题目:
求一个M*N的矩阵的最大子矩阵和。
比如在如下这个矩阵中:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
拥有最大和的子矩阵为:
9 2
-4 1
-1 8
其和为15。
思路:行相加得到一个数组,对数组求最大子数组和。
最大子矩阵的和计算方式可以是这样,从第i行到第j行是满足条件的子矩阵的上下分界,现在需要求出列的分界。所以
可以把第i行到第j行,整行相加到一个数组中就是
a[i][1....n]
+a[i+1][1......n]
+......
+a[j][1....n]
=b[1.............n];
然后对b求其最大连接子数组的和,得到的结果就是矩阵的最大和。
import java.util.Scanner;
public class Main{
public static int Sum(int[] arr){
int res = Integer.MIN_VALUE;
int cur = 0;
for(int i = 0;i < arr.length;i++){
cur += arr[i];
res = Math.max(res,cur);
if(cur < 0)
cur = 0;
}
return res;
}
public static int MaxRect(int[][] arr){
int max = Integer.MIN_VALUE;
int m = arr.length;
int n = arr[0].length;
for(int i = 0 ;i < m ;i++){
int[] temp = new int[n];
for(int j = i;j < m;j++){
for(int k = 0;k < n;k++){
temp[k] += arr[j][k];
}
max = Math.max(max,Sum(temp));
}
}
return max;
}
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int[][] rec = new int[N][N];
for(int i = 0 ;i < N;i++)
for(int j = 0;j < N;j++)
rec[i][j] = sc.nextInt();
System.out.println(MaxRect(rec));
}
}
最大m子段和(m=1时候,就是连续子数组和)