一,最大子数组和
1.什么是子数组:数组中一段连续的序列
2.什么是最大子数组:子数组各个值相加的和最大
这在我们之前学过的最大子数组的分治实现中都有提到。
分治法求解最子数组和的关键步骤就是分解原问题,求跨中点的最大子数组和,重点在于分。
而这次我们采用动态规划来实现,进一步优化分治算法。
二,动态规划步骤
三,动态规划表格及伪代码详解
这里附上我手写的动态规划表格
D[ ]数组用来记录动态规划的最优解;Rec[ ]用来记录尾,也就是如果D[i+1]<=0,我们就抛弃这个尾,从当前位置重新记录;Max当然就是输出最终结果;sub就是最大子数组序列。
下面附上伪代码(感谢一波学校教算法的耿老师,伪代码相当不错)
根据伪代码就可轻松写出代码
到这里,我们就能意识到,既然从尾到头可以计算这个问题,那么从头到尾是否也能可以呢,答案当然是肯定的,原理与此大致相同,在求解动态规划的状态方程上有些区别(毕竟顺序发生变化了嘛)。
如下图我手写的表格所示
四,代码部分
1,从尾到头方法
本次调试非常顺利,一遍就出结果了,唯一要注意的就是伪代码中数组都是从1开始,而我的代码中数组是从0开始,原理都一样,就是给每个遍历数组的标记都减一。
完整代码如下:
import java.util.Arrays;
public class Max_Continuous_Subarray {
public void DP(int[] arr,int n){
//初始化
int [] D=arr.clone();
int [] Rec=new int[n];
for(int i=0;i<n;i++){
Rec[i]=n-1;
}
//动态规划
for(int i=n-2;i>=0;i--){
if(D[i+1]>0){
D[i]=arr[i]+D[i+1];
Rec[i]=Rec[i+1];
}else{
D[i]=arr[i];
Rec[i]=i;
}
}
//输出结果
System.out.println("原数组为:"+Arrays.toString(arr));
System.out.println("动态规划最优解为:"+Arrays.toString(D));
System.out.println("记录尾数组为:"+Arrays.toString(Rec));
Find(D,Rec,arr,n);
}
public void Find(int[] D,int[] Rec,int[] arr,int n){
//查找解
int Smax=D[0];
int l = 0,r=0;
for(int i=1;i<n;i++){
if(Smax<D[i]){
Smax=D[i];
l=i;
r=Rec[i];
}
}
System.out.print("最大子数组序列为:");
for(int i=l;i<=r;i++){
System.out.print(arr[i]+" ");
}
}
public static void main(String[] args) {
Max_Continuous_Subarray dp=new Max_Continuous_Subarray();
int[] arr={31,-41,59,26,-53,58,97,-93,-23,84};
dp.DP(arr,arr.length);
}
}
2,从头到尾方法
下面列出从头到尾方法的代码
import java.util.Arrays;
public class Max_Continuous_Subarray2 {
public void DP(int[] arr,int n){
//初始化
int [] b=arr.clone();
int [] Rec=new int[n];
for(int i=0;i<n;i++){
Rec[i]=0;
}
//动态规划
for(int j=1;j<n;j++){
if(b[j-1]>0){
b[j]=b[j-1]+arr[j];
Rec[j]=Rec[j-1];
}else{
b[j]=arr[j];
Rec[j]=j;
}
}
//输出结果
System.out.println("原数组为:"+Arrays.toString(arr));
System.out.println("动态规划最优解为:"+Arrays.toString(b));
System.out.println("记录尾数组为:"+Arrays.toString(Rec));
Find(b,Rec,arr,n);
}
public void Find(int[] b,int[] Rec,int[] arr,int n){
//查找解
int Smax=b[0];
int l = 0,r=0;//这里原本l代表数组头,r代表数组尾,现在要进行互换
for(int i=1;i<n;i++){
if(Smax<b[i]){
Smax=b[i];
l=i;
r=Rec[i];
}
}
System.out.print("最大子数组序列为:");
for(int i=r;i<=l;i++){
System.out.print(arr[i]+" ");
}
}
public static void main(String[] args) {
Max_Continuous_Subarray2 dp=new Max_Continuous_Subarray2();
int[] arr={31,-41,59,26,-53,58,97,-93,-23,84};
dp.DP(arr,arr.length);
}
}
3,注意项和方法对比
从头到尾方法和从尾到头方法区别并不是很大,主要就在动态规划的状态方程上有区别,另外,还是要注意数组下标都减了1。如下所示。
//动态规划 for(int j=1;j<n;j++){ if(b[j-1]>0){ b[j]=b[j-1]+arr[j]; Rec[j]=Rec[j-1]; }else{ b[j]=arr[j]; Rec[j]=j; } }
为方便区分,我将这两种方法的状态方程代码单独截图列出
有任何问题,还请评论区指正,谢谢。