(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},最大值为132.分析
从后向前,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]);