动态规划法--求数组中最大子集合的和

例题:给定一个数组int[] a = {-9,1,3,5,-1,7,-5,3,1};

计算数组中连续的最大和以及出现的位置

 输出:下标1到5位连续的最大和为15


首先看到这种题目,我的第一反应·就是用冒泡排序的思想去做:

public class zuoye{
    public static void main(String[] args){
	int[] a = {-9,1,3,5,-1,7,-5,3,1};
	int max=a[0];
	int tempValue=0;
	int x=0,y=0;
	for(int i=0;i<a.length-1;i++){
	    tempValue=a[i];
	    for(int j=i+1;j<a.length;j++){
		tempValue+=a[j];
		if(max<tempValue){
			max=tempValue;
			x=i;
			y=j;
		}
	    }
	}
	System.out.println(x+" "+y+" "+max);
    }
}

具体过程如图所示一步步往下走

程序运行结果如下图:



后来我觉得用动态规划法做从时间空间复杂度上来说就有明显的区别。

用冒泡排序的思想做,时间复杂度(最坏的情况)为O(n^2

若果用动态规划法要做时间复杂度只有O(n)


首先来说下动态规划法的概念(难以理解,我们还是看下)

动态规划求解的基本步骤:

能采用动态规划求解的问题一般要具有3个性质:

(1)最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

(2)无后效性:即某阶段的状态一定确立,就不受这个状态以后决策的影响,也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

(3)有重叠子问题:即子问题之间是不独立的,一个字问题在下一阶段的决策中可能被多次使用到(该性质不是动态规划的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

 

动态规划算法有一定自己的模式,一般要经历如下几个步骤:

(1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段,在划分阶段注重划分后的阶段一定要是有序或者是可排序的,否则问题就无法求解。

(2)确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。

(3)确定决策并写出状态转移方程:因为决策和状态转移方程有着天然的联系。状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以决定了决策,状态转移方程也就可以写出。

但事实上常常是反过来做,根据相邻的两个阶段的状态之间的关系来确定决策方法和转移方程。

(4)寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或者边界条件。

 

动态规划法的基本思想:

将待求解的基本问题分为若干个子问题,按照顺序求解子阶段,前一个问题的解,为后一子问题的求解提供有用的信息。在求解任何问题时,列出各种可能的局部解,通过决策保留那些可能达到最优的局部解,丢弃其他局部解。依次解决各个子问题,最后一个子问题就是初始化的解。

 

动态规划采用的基本办法:

为了节约重复求相同子问题的时间,引入一个数组或者一组变量,不管它们是否对最终解有用,把所有子问题的解存于该数组或者这组变量中。


我觉得最好理解的为上面的基本方法:

/*如果用函数f(i)表示以第i个数字结尾的子数组的最大和,那么我们需要求出max(f[0...n])。 
我们可以给出如下递归公式求f(i) 
     |-- array[i] 如果i==0或者f(i-1)<0 
f(i)=| 
     |-- f(i-1) + array[i] 如果f(i-1)>0 
这个公式的意义: 
   当以第(i-1)个数字为结尾的子数组中所有数字的和f(i-1)小于0时,如果把这个负数和第i个数相加,得到的结果反而不第i个数本身还要小,所以这种情况下最大子数组和是第i个数本身。 
 如果以第(i-1)个数字为结尾的子数组中所有数字的和f(i-1)大于0,与第i个数累加就得到了以第i个数结尾的子数组中所有数字的和。 
*/  

public class array {
	public static void main(String[] args){
	int[] array = {-9,1,3,5,-1,7,-5,3,1};
	int len=array.length;
	int[] c=new int[len];//引入一个数组
	int max = -1000;//用来记录数组c[]中的最大值  
	int start = 0;//记录数组中子数组的最大和的开始位置  
	int end = 0;//记录数组中子数组的最大和的结束位置  
	int tmp = 0;//中间变量
	c[0] = array[0];
	for (int i = 1; i < len; ++i)
	{
		if (c[i - 1] > 0)
		{
			c[i] = c[i - 1] + array[i];
		}
		else
		{
			c[i] = array[i];
			tmp = i;
		}
		if (c[i] > max)
		{
			max = c[i];
			start = tmp;
			end = i;
		}
	}
	System.out.println(start+"~"+end+"Max is:"+max);
   }
}
上面代码就是用动态规划法来求解的利用了一个数组C来保存临时产生的值,只用了一层for循环,时间复杂度为O(n)。大大的加快了速度。

结果是一样的,分析过程就由你们自己去分析了^*^....

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值