LIS及扩展

                (1)LIS

                          1.题目描述

                             一个整数数组,求其中最长递增子序列

                          2.思路1

                             定义dp[0....n],dp[i]为从0到i最长递增序列(不一定包含元素a[i]),所以dp[n]就是结果。

                             因为不必须包含a[i],那么状态转移方程为:dp[i] = max{dp[j]+1},a[i]>a[j],j<i;  dp[i] = max{dp[j]},a[i]<=a[j]j<i 

                             代码:

		int[] a = {80,15,324,45,123};
		
		int[] dp = new int[a.length+1];
		for(int i=1; i<=a.length; i++)
			dp[i] = 1;
		
		for(int i=1; i<=a.length; i++)
		{
			int maxInclude = 1;
			int maxNotIn = dp[1];
			for(int j=1; j<i; j++)
			{
				if(a[i-1]>a[j-1])
					maxInclude = Math.max(maxInclude, dp[j]+1);
				else
					maxNotIn = Math.max(maxNotIn, dp[j]);
			}
			
			dp[i] = Math.max(maxInclude, maxNotIn);
		}
		System.out.println(dp[a.length]);

                          3.思路2

                             定义dp[0....n],dp[i]为从0到i最长递增序列(一定包含元素a[i]),所以dp[n]中最大值才是结果

                             因为必须包含a[i],那么状态转移方程为:dp[i] = max{dp[j]+1},a[i]>a[j],j<i;  dp[i] = 1,a[i]<=a[j],j<i 

                             代码:

		int[] a = {80,15,324,45,123};
		
		int[] dp = new int[a.length+1];
		for(int i=1; i<=a.length; i++)
			dp[i] = 1;
		
		for(int i=1; i<=a.length; i++)
		{
			int max = 1;
			for(int j=1; j<i; j++)
			{
				if(a[i-1]>a[j-1])
					max = Math.max(max,dp[j]+1);
			}
			dp[i] = max;
		}
		
		int longest = 1;
		for(int x:dp)
			longest = Math.max(longest, x);
		
		System.out.println(longest);


                          3.记录构成最长子序列的数据

                              1.思路

                                 对于dp记录的是必须包含a[i]的情况,那么如果dp[i]是最大值,那么i就是递增子序列的最后一个值的下标,又已经子序列长度longest,且前面的数据根据定义必然比a[i]小,所以从i到0遍历,找到longest个比a[i]小的元素即可。由于考虑到正序输出,所以可以使用栈

                              2.代码

		int[] a = {80,15,65,324,45,123};
		
		int[] dp = new int[a.length+1];
		for(int i=1; i<=a.length; i++)
			dp[i] = 1;
		
		
		for(int i=1; i<=a.length; i++)
		{
			int max = 1;
			for(int j=1; j<i; j++)
			{
				if(a[i-1]>a[j-1])
					max = Math.max(max,dp[j]+1);
			}
			dp[i] = max;
		}
		
		int longest = 1;//记录最长递增子序列的长度
		int index = 1;//记录递增子序列的最后一个元素的下标
		for(int i=1; i<=a.length; i++)
		{
			if(longest<dp[i])
			{
				longest = dp[i];
				index = i;
			}
		}
		
		System.out.println(longest);
		
		//最后一个元素先入栈,并让longest--
		Stack<Integer> stack = new Stack<Integer>();
		stack.push(a[index-1]);
		longest--;
		
		//从后向前,找到longest-1个比a[i]小的元素即可
		for(int i=index-1; i>=0 && longest>0; i--)
		{
			if(a[i]<a[index-1])
			{
				stack.push(a[i]);
				longest--;
			}
		}
		//顺序打印数据
		while(!stack.isEmpty())
			System.out.print(stack.pop()+" ");



                (2)扩展:求递增连续子序列

                          1.思路

                             由于是求连续的,那么只用和前一个比较即可,可以得出状态转移方程为:dp[i] = dp[i-1]+1,a[i]>a[i-1];dp[i] = 1,a[i]<=a[i-1]

                             那么结果就是dp的最大值

                           2.代码

                int[] a = {80,15,65,324,45,123};
		
		int[] dp = new int[a.length+1];
		for(int i=1; i<=a.length; i++)
			dp[i] = 1;
		
		int longest = 1;
		for(int i=2; i<=a.length; i++)
		{
			if(a[i-1]>a[i-1-1])
				dp[i] = dp[i-1]+1;
			else
				dp[i] = 1;
			
			longest = Math.max(longest, dp[i]);
		}
		
		System.out.println(longest);


                (2)扩展:求子数组最大和

                          1.思路

                             定义dp是包含最后一个元素的子数组和的最大值,那么就有2种情况:1,dp[i-1]<=0,那么肯定a[i]是最大和了  2,dp[i-1]>0,因为必须要加上a[i],那么最大和就是dp[i-1]+a[i],所以状态转移方程就是:dp[i] = dp[i-1]+a[i],dp[i-1]>0;dp[i] = a[i],dp[i-1]<=0

                           2.代码

		int[] a = {1, -2, 3, 10, -4, 7, 2, -5};
		
		int[] dp = new int[a.length];
		
		int maxSum = 0;
		for(int i=1; i<a.length; i++)
		{
			if(dp[i-1]<=0)
				dp[i] = a[i];
			else
				dp[i] = dp[i-1]+a[i];
			
			maxSum = Math.max(maxSum, dp[i]);
		}
		
		System.out.println(maxSum);
                         3.记录构成元素

                            此情况下,如果dp[i]最大,那么i就是最大子数组结束的下标,因为是连续的,所以下标依次递减,最大和和依次减去a[j]直至等于0即可(如果需要正序,使用栈)

		int[] a = {1, -2, 3, 10, -4, 7, 2, -5};
		
		int[] dp = new int[a.length+1];
		
		int maxSum = 0;
		int endIndex = 0;
		for(int i=1; i<a.length; i++)
		{
			if(dp[i-1]<=0)
				dp[i] = a[i];
			else
				dp[i] = dp[i-1]+a[i];
			
			if(maxSum < dp[i])
			{
				maxSum = dp[i];
				endIndex = i;
			}
		}
		
		System.out.println(maxSum);
		
		while(maxSum > 0)
		{
			System.out.print(a[endIndex]+" ");
			maxSum = maxSum - a[endIndex];
			endIndex--;
		}

                         4.思路2

                            从后向前,使用start[i]记录以a[i]为开始的子数组的最大和,dp[i]记录从i到结尾子数组最大和。显然,状态转移方程dp[i] = max{a[i],start[i-1]+a[i],dp[i-1]}
                            代码

		int[] a = {1, -2, 3, 10, -4, 7, 2, -5};
		
		int[] dp = new int[a.length];
		int[] start = new int[a.length];

		dp[a.length-1] = a[a.length-1];
		start[a.length-1] = a[a.length-1];
		
		for(int i=a.length-2; i>=0; i--)
		{
			start[i] = Math.max(a[i], a[i]+start[i+1]);
			dp[i] = Math.max(dp[i+1], start[i]);
		}
		System.out.println(dp[0]);


                (3)扩展:求数组中元素和它右边元素差的最大值

                          1.问题描述

                              求数组中元素和它右边元素差的最大值,如{2,9,5,8,16,3,15},最大值为13

                          2.分析

                             从后向前,dp[i]记录从i到最后差的最大值,使用min记录从i到最后元素中最小的值,那么状态转移方程就是:dp[i] = a[i]-min,a[i]-min>dp[i-1];

                            dp[i] = dp[i-1],a[i]-min<=dp[i-1]

                             代码:

		int[] a = {2,9,5,8,16,3,15};
		int[] dp = new int[a.length-1];
		
		int min = a[a.length-1]<a[a.length-2]?a[a.length-1]:a[a.length-2];
		dp[a.length-2] = a[a.length-2]-a[a.length-1];
		
		for(int i=a.length-3; i>=0; i--)
		{
			if(a[i]-min>dp[i+1])
				dp[i] = a[i]-min;
			else
				dp[i] = dp[i+1];
			
			min = Math.max(min, a[i]);
		}	
		
		System.out.println(dp[0]);



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值