递减递增交替的任意大小的最大和子序列
给定一个整数数组arr[] ,找到总和最大的子序列,其元素首先减少,然后增加,反之亦然,子序列可以在主序列的任何地方开始,不一定在主序列的第一个元素。
A sequence {x1, x2, .. xn} is an alternating sequence if its elements satisfy one of the following relations (As described in the article Longest alternating subsequence):
Two adjacent elements that are equal are not counted as alternating.
x1 < x2 > x3 < x4 > x5 < …. xn
or
x1 > x2 < x3 > x4 < x5 > …. xn
例子 :
Input: arr[] = {4, 8, 2, 5, 6, 8}
Output: 26
Explanation: Alternating subsequence with maximum sum is {4, 8, 6, 8}. Sum = 4 + 8 + 6 + 8 = 26
Input: arr[] = {0, 1, 1, 1, 3, 2, 5}
Output: 11
Explanation: Alternating subsequence with maximum sum is {1, 3, 2, 5}. Sum = 1 + 3 + 2 + 5 = 11
Input: arr[] = {1, 4, 5}
Output: 9
Explanation: Alternating subsequence with maximum sum is {4, 5}. Sum = 4 + 5 = 9
Input: arr[] = {1, 0, 1, 0, 0, 3}
Output: 5
Explanation: Alternating subsequence with maximum sum is {1, 0, 1, 0, 3}. Sum = 1 + 0 + 1 + 0 + 3 = 5
Input: arr[] = {5, 5}
Output: 5
Explanation: Alternating subsequence with maximum sum is {5}. Sum = 5
这个问题是最大和交替子序列问题的扩展。与上一个问题不同,现在我们需要计算交替子序列的最大和,无论它在主序列中的哪个位置,也不管它是以两个元素升序还是两个降序元素开始。
方法:这个问题可以通过动态规划结合回溯来解决。假设,我们有一个包含任意N个元素的数组arr[] 。
- 我们可以将每个 arr[i] 元素视为交替序列的终止元素。
- 可以有许多交替的子序列,其最后一个元素是 arr[i] ,但其中一个肯定是总和最大的序列。
- 此外,以arr[i]结尾的交替子序列的最大和不一定大于以arr[i-1]结尾的交替子序列的最大和。这是根据回溯技术实现算法的基础。
- 有一个长度为N的数组说maxSum[] ,它将存储在数组arr[]中的每个元素处结束的所有最大和。具体来说,maxSum[i] 将存储以 arr[i] 值结尾的交替子序列的最大和。
- 我们将使用另一个数组before[]来存储每个交替子序列的最后一个元素之前的值,并具有最大和。
For example, an array arr[] = {1, 5, 7, 3, 4, 5} will have an alternating subsequence {5, 7, 3, 4} whose maximum sum is 5 + 7 + 3 + 4 = 19.
The subsequence {5, 7, 3, 4} ends at the value 4, which has index 4 in the array arr[].
So we have arr[4] = 4, maxSum[4] = 19, before[4] = 3 (because arr[3] = 3).
Those values will be calculated and saved into two arrays maxSum[] and before[] during the calculation and can be reused as needed.
使用以下步骤来实施该方法:
- 使用循环遍历数组 arr[] 的每个元素。每次我们遍历一个 arr[i] 元素时,我们都会查看它之前的元素:
i Loop traversal from 1 to N-1 (There’s no need to start at position 0, because that’s the base case):
j Loop traversal from 0 to i-1:
- 在每个状态i和j ,我们将根据动态规划原理重用任何0 <= j < i的 maxSum[j]:
if:
(arr[i] > arr[j] && arr[before[j]] > arr[j])
or
(arr[i] < arr[j] && arr[before[j]] < arr[j])
then:
sum = arr[i] + maxSum[j]
- 如果 maxSum[j]不满足上述条件,则可以是两个相等的元素。由于两个相等的元素不被视为交替序列,我们只取其中一个。
if:
arr[i] == arr[j]
then:
sum = maxSum[j]
- 或者它可能不是以上任何一种,例如具有两个以上的元素以增加或具有两个以上的元素以递减顺序。
Example {1, 5, 7, 3, 4, 5}. Suppose we are at i = 5, j = 4.
- Then before[4] is the 3-rd element, and (arr[before[j]], arr[j], arr[i]) = (arr[before[4]], arr[4], arr[5]) = (arr[3], arr[4], arr[5]) = (3, 4, 5).
- The above one is not a valid alternating subsequence, meaning that before[j] is not the index of a valid value.
- We will look at the preceding element of arr[before[j]] in its alternating sequence: before[before[j]] = before[3] = 2, and arr[2] with arr[4] and arr[5] form a valid subsequence (7, 4, 5).
- So we get a value of arr[5] + arr[4] + maxSum[2], which is the final sum of state {i = 5, j = 4}.
- We need to compare it with other {i and j} states (like {i = 5, j = 3}, {i = 5, j = 2}…) to get the final result for maxSum[5].
- Then save j into the before[5] if it precedes arr[5] and helps arr[5] form a valid subsequence with a maximum sum at state 5.
看一下下面的插图,可以清楚地了解这个想法。
index : 0 1 2 3 4 5 || N = 6
arr[] : 1, 5, 7, 3, 4, 5
—————————————————————————————————-
maxSum[0] : 1 0 0 0 0 0 = 1 base case
before[-1] : -1 _ _ _ _ _
maxSum[1] : 1 6 0 0 0 0 = 6 because 1 + 5
before[1] : -1 0 _ _ _ _
maxSum[2] : 1 6 12 0 0 0 = 12 because 1, 5, 7 isn’t alternating subsequence, and just 5 & 7
before[2] : -1 0 1 _ _ _ is valid, we use backtracking at index 2 so
we will go through the following subsequence: {1, 7}, {5, 7}. We have {5, 7} because before[1] is index 0,
we continue to find value at before[before[1]] = -1, whose value is 0, so 0 + 5 + 7 = 12 > 1 + 7.
maxSum[3] : 1 6 12 15 0 0 = 15 because 5, 7, 3 is valid alternating subsequence,
before[3] : -1 0 1 2 _ _ so 3 + max(maxSum[2], maxSum[1], maxSum[0]) = 3 + 12.
maxSum[4] : 1 6 12 15 19 0 = 19 because 5, 7, 3, 4 is valid alternating subsequence,
before[4] : -1 0 1 2 3 _ so 4 + max(maxSum[3], maxSum[2],… maxSum[0]) = 4 + 15.
maxSum[5] : 1 6 12 15 19 21 = 21, arr[5] cannot be the next element of maxSum[4] because 3, 4, 5
before[5] : -1 0 1 2 3 2 isn’t alternating subsequence. So we need use backtracking here.
We will treat the value 3 as an invalid value for the alternating subsequence ending in arr[5].
We need to find the preceding element of before[arr[4]] in the sum of maxSum[4] recursively,
that is index 2. Now 7, 4, 5 have formed a valid alternating subsequence.
So we have 5 + max(backtracking(index 4), maxSum[3], maxSum[2],…) = 5 + 16 ( because 4 + 7 + 5)
Then, final max sum of alternating subsequence of arr is max element of “maxSum” array.
Output: 21
下面是上述方法的实现:
C++
// C++ code to implement the above approach
#include
using namespace std;
// Function for backtracking
int backtracking(int arr[], int maxSum[], int before[],
int N, int root, int bef_root,
int bbef_root)
{
// {root, bef_root} represents state{i, j}
// bbef_root represent before[before[j]]
// We ignore the invalid before[j] index
// Base case:
if (bbef_root == -1)
return arr[bef_root];
// The case of a subsequence with
// alternating parts:
if ((arr[root] > arr[bef_root]
&& arr[bbef_root] > arr[bef_root])
|| (arr[root] < arr[bef_root]
&& arr[bbef_root] < arr[bef_root])) {
return arr[bef_root] + maxSum[bbef_root];
}
// case (arr[bef_root] == arr[bbef_root])
else {
return backtracking(arr, maxSum, before, N, root,
bef_root, before[bbef_root]);
}
}
int maxSumAlternatingSubsequence(int arr[], int N)
{
// Max alternating subsequence sum
// ending at arr[i].
int maxSum[N];
// Array to store the index of the element
// preceding the last element at maxSum[i]
int before[N];
// Value initialization for arrays:
fill_n(&maxSum[0], N, 0);
maxSum[0] = arr[0];
before[0] = -1;
// Iterate over the array:
for (int i = 1; i < N; i++)
for (int j = 0; j < i; j++) {
int currentMax = 0;
if ((arr[i] > arr[j]
&& arr[before[j]] > arr[j])
|| (arr[i] < arr[j]
&& arr[before[j]] < arr[j])
|| before[j] == -1) {
// Whenever an element is
// between two smaller elements
// or between two larger elements,
// it is an alternating sequence.
// When the preceding index of j is -1,
// we need to treat it explicitly,
// because -1 is not a valid index.
currentMax = (arr[i] == arr[j])
? maxSum[j]
: arr[i] + maxSum[j];
}
else if (arr[i] == arr[j]) {
// If arr[i] is equal to arr[j] then
// only take it once,
// before[j] cannot be equal to -1.
currentMax = maxSum[j];
}
else {
// Perform backtracking
// If three adjacent elements
// are increasing or decreasing.
currentMax = arr[i]
+ backtracking(
arr, maxSum, before, N, i,
j, before[before[j]]);
}
if (currentMax >= maxSum[i]) {
// Stores the maximum sum and
// the index preceding
// the last element
// at position i of
// current alternating subsequence
// after each iteration.
maxSum[i] = currentMax;
before[i] = j;
}
}
// get max result in array
return *max_element(maxSum, maxSum + N);
}
// Driver code
int main()
{
int arr[] = { 1, 5, 7, 3, 4, 5 };
int N = sizeof(arr) / sizeof(int);
// Maximum sum of alternating subsequence
// of array arr[]
cout << maxSumAlternatingSubsequence(arr, N)
<< endl;
return 0;
}
Java
// Java code to implement the above approach
import java.io.*;
class GFG{
// Function for backtracking
static int backtracking(int arr[], int maxSum[],
int before[], int N, int root,
int bef_root, int bbef_root)
{
// {root, bef_root} represents state{i, j}
// bbef_root represent before[before[j]]
// We ignore the invalid before[j] index
// Base case:
if (bbef_root == -1)
return arr[bef_root];
// The case of a subsequence with
// alternating parts:
if ((arr[root] > arr[bef_root] &&
arr[bbef_root] > arr[bef_root]) ||
(arr[root] < arr[bef_root] &&
arr[bbef_root] < arr[bef_root]))
{
return arr[bef_root] + maxSum[bbef_root];
}
// case (arr[bef_root] == arr[bbef_root])
else
{
return backtracking(arr, maxSum, before, N,
root, bef_root,
before[bbef_root]);
}
}
static int maxSumAlternatingSubsequence(int arr[],
int N)
{
// Max alternating subsequence sum
// ending at arr[i].
int maxSum[] = new int[N];
// Array to store the index of the element
// preceding the last element at maxSum[i]
int before[] = new int[N];
// Value initialization for arrays:
maxSum[0] = arr[0];
before[0] = -1;
// Iterate over the array:
for(int i = 1; i < N; i++)
for(int j = 0; j < i; j++)
{
int currentMax = 0;
if ((arr[i] > arr[j] && before[j] != -1 &&
arr[before[j]] > arr[j]) ||
(arr[i] < arr[j] && before[j] != -1 &&
arr[before[j]] < arr[j]) || before[j] == -1)
{
// Whenever an element is
// between two smaller elements
// or between two larger elements,
// it is an alternating sequence.
// When the preceding index of j is -1,
// we need to treat it explicitly,
// because -1 is not a valid index.
currentMax = (arr[i] == arr[j]) ? maxSum[j] :
arr[i] + maxSum[j];
}
else if (arr[i] == arr[j])
{
// If arr[i] is equal to arr[j] then
// only take it once,
// before[j] cannot be equal to -1.
currentMax = maxSum[j];
}
else
{
// Perform backtracking
// If three adjacent elements
// are increasing or decreasing.
currentMax = arr[i] + backtracking(arr, maxSum,
before, N, i, j,
before[before[j]]);
}
if (currentMax >= maxSum[i])
{
// Stores the maximum sum and the index
// preceding the last element at position
// i of current alternating subsequence
// after each iteration.
maxSum[i] = currentMax;
before[i] = j;
}
}
// get max result in array
int maxi = 0;
for(int i = 0; i < N; i++)
{
maxi = Math.max(maxSum[i], maxi);
}
return maxi;
}
// Driver code
public static void main(String[] args)
{
int arr[] = { 1, 5, 7, 3, 4, 5 };
int N = arr.length;
// Maximum sum of alternating subsequence
// of array arr[]
System.out.println(
maxSumAlternatingSubsequence(arr, N));
}
}
// This code is contributed by Potta Lokesh
Python3
# Python code to implement the above approach
# Function for backtracking
def backtracking(arr, maxSum, before, N, root, bef_root, bbef_root):
# {root, bef_root} represents state{i, j}
# bbef_root represent before[before[j]]
# We ignore the invalid before[j] index
# Base case:
if (bbef_root == -1):
return arr[bef_root]
# The case of a subsequence with
# alternating parts:
if (arr[root] > arr[bef_root] and arr[bbef_root] > arr[bef_root]) or (arr[root] < arr[bef_root] and arr[bbef_root] < arr[bef_root]):
return arr[bef_root] + maxSum[bbef_root]
# case (arr[bef_root] == arr[bbef_root])
else:
return backtracking(arr, maxSum, before, N,
root, bef_root,
before[bbef_root])
def maxSumAlternatingSubsequence(arr, N):
# Max alternating subsequence sum
# ending at arr[i].
maxSum = [0] * N
# Array to store the index of the element
# preceding the last element at maxSum[i]
before = [0] * N
# Value initialization for arrays:
maxSum[0] = arr[0]
before[0] = -1
# Iterate over the array:
for i in range(N):
for j in range(i):
currentMax = 0
if (arr[i] > arr[j] and before[j] != -1 and arr[before[j]] > arr[j]) or (arr[i] < arr[j] and before[j] != -1 and arr[before[j]] < arr[j]) or before[j] == -1:
# Whenever an element is
# between two smaller elements
# or between two larger elements,
# it is an alternating sequence.
# When the preceding index of j is -1,
# we need to treat it explicitly,
# because -1 is not a valid index.
currentMax = maxSum[j] if (
arr[i] == arr[j]) else arr[i] + maxSum[j]
elif (arr[i] == arr[j]):
# If arr[i] is equal to arr[j] then
# only take it once,
# before[j] cannot be equal to -1.
currentMax = maxSum[j]
else:
# Perform backtracking
# If three adjacent elements
# are increasing or decreasing.
currentMax = arr[i] + backtracking(arr, maxSum,
before, N, i, j,
before[before[j]])
if (currentMax >= maxSum[i]):
# Stores the maximum sum and the index
# preceding the last element at position
# i of current alternating subsequence
# after each iteration.
maxSum[i] = currentMax
before[i] = j
# get max result in array
maxi = 0
for i in range(N):
maxi = max(maxSum[i], maxi)
return maxi
# Driver code
arr = [1, 5, 7, 3, 4, 5]
N = len(arr)
# Maximum sum of alternating subsequence
# of array arr
print(maxSumAlternatingSubsequence(arr, N))
# This code is contributed by gfgking
C#
// C# program for above approach
using System;
class GFG{
// Function for backtracking
static int backtracking(int []arr, int []maxSum,
int []before, int N, int root,
int bef_root, int bbef_root)
{
// {root, bef_root} represents state{i, j}
// bbef_root represent before[before[j]]
// We ignore the invalid before[j] index
// Base case:
if (bbef_root == -1)
return arr[bef_root];
// The case of a subsequence with
// alternating parts:
if ((arr[root] > arr[bef_root] &&
arr[bbef_root] > arr[bef_root]) ||
(arr[root] < arr[bef_root] &&
arr[bbef_root] < arr[bef_root]))
{
return arr[bef_root] + maxSum[bbef_root];
}
// case (arr[bef_root] == arr[bbef_root])
else
{
return backtracking(arr, maxSum, before, N,
root, bef_root,
before[bbef_root]);
}
}
static int maxSumAlternatingSubsequence(int []arr,
int N)
{
// Max alternating subsequence sum
// ending at arr[i].
int []maxSum = new int[N];
// Array to store the index of the element
// preceding the last element at maxSum[i]
int []before = new int[N];
// Value initialization for arrays:
maxSum[0] = arr[0];
before[0] = -1;
// Iterate over the array:
for(int i = 1; i < N; i++)
for(int j = 0; j < i; j++)
{
int currentMax = 0;
if ((arr[i] > arr[j] && before[j] != -1 &&
arr[before[j]] > arr[j]) ||
(arr[i] < arr[j] && before[j] != -1 &&
arr[before[j]] < arr[j]) || before[j] == -1)
{
// Whenever an element is
// between two smaller elements
// or between two larger elements,
// it is an alternating sequence.
// When the preceding index of j is -1,
// we need to treat it explicitly,
// because -1 is not a valid index.
currentMax = (arr[i] == arr[j]) ? maxSum[j] :
arr[i] + maxSum[j];
}
else if (arr[i] == arr[j])
{
// If arr[i] is equal to arr[j] then
// only take it once,
// before[j] cannot be equal to -1.
currentMax = maxSum[j];
}
else
{
// Perform backtracking
// If three adjacent elements
// are increasing or decreasing.
currentMax = arr[i] + backtracking(arr, maxSum,
before, N, i, j,
before[before[j]]);
}
if (currentMax >= maxSum[i])
{
// Stores the maximum sum and the index
// preceding the last element at position
// i of current alternating subsequence
// after each iteration.
maxSum[i] = currentMax;
before[i] = j;
}
}
// get max result in array
int maxi = 0;
for(int i = 0; i < N; i++)
{
maxi = Math.Max(maxSum[i], maxi);
}
return maxi;
}
// Driver Code
public static void Main()
{
int []arr = { 1, 5, 7, 3, 4, 5 };
int N = arr.Length;
// Maximum sum of alternating subsequence
// of array arr[]
Console.Write(
maxSumAlternatingSubsequence(arr, N));
}
}
// This code is contributed by Samim Hossain Mondal.
Javascript
21
时间复杂度: O(N 2 ) 其中 N 是数组的长度。
辅助空间: O(N)