动态规划算法学习十例之六

一、数组的最大子段和问题:

给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。
当所给的整数均为负数时定义子段和为0,依此定义,所求的最大值为:

Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n
例如,当(a1,a2,a3,a4,a4,a6)=(-2,11,-4,13,-5,-2)时,最大子段和为20。'

我们令一个数组b,b[j]表示前j个元素能组成的最大和。如果b[j-1]大于零,则不管a[j]的情况,b[j-1]都可以向正方向影响,因此可以将a[j]加在b[j-1]上。如果b[j-1]小于零,则不管a[j]再大, 都会产生负影响,因此我们还不如直接令b[j]=a[j]。

import java.util.*;
public class LargestSubsegmentSum
{
public static void main(String[] args)
{

/**
*从键盘输入所要求的序列的长度n
*/
Scanner in=new Scanner(System.in);

int n=in.nextInt();
/**
*从键盘输入所要求的序列,存储在a[n]中
*/
int[] a=new int[n];
int i;
for(i=0;i< n;i++)
{
a[i]=in.nextInt();
}
/**
*求解最大子段和存在maxSum中
*/

int maxSum=0;
/* 我们令一个数组b,b[j]表示前j个元素能组成的最大和。如果b[j-1]大于零,则不管a[j]的情况,
* b[j-1]都可以向正方向影响,因此可以将a[j]加在b[j-1]上。如果b[j-1]小于零,则不管a[j]再大,
* 都会产生负影响,因此我们还不如直接令b[j]=a[j]。
*/
int[] b=new int[n];
b[0]=a[0];
for(int j=1;j< n;j++)
{
if(b[j-1]>0){
b[j]=b[j-1]+a[j];

}
else
{
b[j]=a[j];

}
if(b[j]>maxSum)
maxSum=b[j];
}

System.out.println("The largest sub-segment sum is(最大子段和是):"+maxSum);

}
}


运行:
C:\test>java LargestSubsegmentSum
15
1 2 -1 1 3 2 -2 3 -1 5 -7 3 2 -2 -1
The largest sub-segment sum is(最大子段和是):13

C:\test>java LargestSubsegmentSum
6
-2 11 -4 13 -5 -2
The largest sub-segment sum is(最大子段和是):20


二、最大子矩阵问题
在一个矩阵里面找它的子矩阵,使得子矩阵中各元素数值之和到达最大。

再来看一下上面提到的最大子段和问题,令b[j]表示从a[0]~a[j]的最大子段和,b[j]的当前值只有两种情况,
(1) 最大子段一直连续到a[j]
(2) 以a[j]为起点的子段
由此我们得出b[j]的状态转移方程为:b[j]=max{b[j-1]+a[j],a[j]},
所求的最大子段和为max{b[j],0<=j<n}。进一步我们可以将b[]数组用一个变量代替。得出的算法如下:

public int maxSubArray(int n,int a[])
{
int b=0,sum=-10000000;
for(int i=0;i< n;i++)
{
if(b>0) b+=a[i];
else b=a[i];
if(b>sum) sum=b;
}
return sum;
}
这里用到了动态规划。现在回到我们的最初的最大子矩阵的问题,这个问题与上面所提到的最大子断有什么联系呢?

假设最大子矩阵的结果为从第r行到k行、从第i列到j列的子矩阵,如下所示(ari表示a[r][i],假设数组下标从1开始):

[img]http://dl.iteye.com/upload/attachment/0074/5926/76e7430e-189e-3485-81a3-52cf54d70e0e.gif[/img]

那么我们将从第r行到第k行的每一行中相同列的加起来,可以得到一个一维数组如下:
(ar1+……+ak1, ar2+……+ak2, ……,arn+……+akn)

由此我们可以看出最后所求的就是此一维数组的最大子段和问题,到此我们已经将问题转化为上面的已经解决了的问题了。

import java.util.Scanner;
public class Main
{
private int maxSubArray(int n,int a[])
{
int b=0,sum=-10000000;
for(int i=0;i< n;i++)
{
if(b>0) b+=a[i];
else b=a[i];
if(b>sum) sum=b;
}
return sum;
}
private int maxSubMatrix(int n,int[][] array)
{
int i,j,k,max=0,sum=-100000000;
int b[]=new int[101];
for(i=0;i< n;i++)
{
for(k=0;k< n;k++)//初始化b[]
{
b[k]=0;
}
for(j=i;j< n;j++)//把第i行到第j行相加,对每一次相加求出最大值
{
for(k=0;k< n;k++)
{
b[k]+=array[j][k];
}
max=maxSubArray(k,b);
if(max>sum)
{
sum=max;
}
}
}
return sum;
}
public static void main(String args[])
{
Main p=new Main();
Scanner cin=new Scanner(System.in);
int n=0;
int[][] array=new int[101][101];
while(cin.hasNext())
{
n=cin.nextInt();
for(int i=0;i< n;i++)
{
for(int j=0;j< n;j++)
{
array[i][j]=cin.nextInt();
}
}
System.out.println(p.maxSubMatrix(n,array));
}
}
}


运行:
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2

15

下载源码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值