LIS及扩展

来源:互联网 发布:三国志11自制武将数据 编辑:程序博客网 时间:2024/04/29 20:45

                (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);elsemaxNotIn = 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;elsedp[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];elsedp[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];elsedp[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;elsedp[i] = dp[i+1];min = Math.max(min, a[i]);}System.out.println(dp[0]);



0 0