给定一个长度为 N 的数组 arr[],任务是找到该数组的每个可能子数组的最大元素的总和。
例子:
Input : arr[] = {1, 3, 1, 7}
Output : 42
Max of all sub-arrays:
{1} - 1
{1, 3} - 3
{1, 3, 1} - 3
{1, 3, 1, 7} - 7
{3} - 3
{3, 1} - 3
{3, 1, 7} - 7
{1} - 1
{1, 7} - 7
{7} - 7
1 + 3 + 3 + 7 + 3 + 3 + 7 + 1 + 7 + 7 = 42
Input : arr[] = {1, 1, 1, 1, 1}
Output : 15
我们已经在本文中讨论了使用堆栈解决此问题的 O(N) 方法。
方法 :
在本文中,我们将学习如何使用分治法来解决这个问题。
让我们假设第i个索引处的元素是最大的。对于包含索引“i”的任何子数组,“i”处的元素将始终是子数组中的最大值。
如果第i个索引处的元素最大,我们可以有把握地说,第 i 个索引的元素在 (i+1)*(Ni) 个子数组中最大。因此,它的总贡献将是 arr[i]*(i+1)*(Ni)。现在,我们将数组分为两部分,(0, i-1) 和 (i+1, N-1) 并分别对它们应用相同的算法。
所以我们一般的递推关系将是:
maxSumSubarray(arr, l, r) = arr[i]*(r-i+1)*(i-l+1)
+ maxSumSubarray(arr, l, i-1)
+ maxSumSubarray(arr, i+1, r)
where i is index of maximum element in range [l, r].
现在,我们需要一种有效回答 rangeMax() 查询的方法。段树将是回答此查询的有效方法。我们最多需要回答这个查询 N 次。因此,我们的分治算法的时间复杂度为 O(Nlog(N))。
如果我们必须回答“所有子数组的最小值之和”这个问题,那么我们将使用段树来回答 rangeMin() 查询。为此,您可以查看文章段树范围的最小值。
下面是实现代码:
C++
// C++ implementation of the above approach
#include
#define seg_max 51
using namespace std;
// Array to store segment tree.
// In first we will store the maximum
// of a range
// In second, we will store index of
// that range
pair seg_tree[seg_max];
// Size of array declared global
// to maintain simplicity in code
int n;
// Function to build segment tree
pair buildMaxTree(int l, int r, int i, int arr[])
{
// Base case
if (l == r) {
seg_tree[i] = { arr[l], l };
return seg_tree[i];
}
// Finding the maximum among left and right child
seg_tree[i] = max(buildMaxTree(l, (l + r) / 2, 2 * i + 1, arr),
buildMaxTree((l + r) / 2 + 1, r, 2 * i + 2, arr));
// Returning the maximum to parent
return seg_tree[i];
}
// Function to perform range-max query in segment tree
pair rangeMax(int l, int r, int arr[],
int i = 0, int sl = 0, int sr = n - 1)
{
// Base cases
if (sr < l || sl > r)
return { INT_MIN, -1 };
if (sl >= l and sr <= r)
return seg_tree[i];
// Finding the maximum among left and right child
return max(rangeMax(l, r, arr, 2 * i + 1, sl, (sl + sr) / 2),
rangeMax(l, r, arr, 2 * i + 2, (sl + sr) / 2 + 1, sr));
}
// Function to find maximum sum subarray
int maxSumSubarray(int arr[], int l = 0, int r = n - 1)
{
// base case
if (l > r)
return 0;
// range-max query to determine
// largest in the range.
pair a = rangeMax(l, r, arr);
// divide the array in two parts
return a.first * (r - a.second + 1) * (a.second - l + 1)
+ maxSumSubarray(arr, l, a.second - 1)
+ maxSumSubarray(arr, a.second + 1, r);
}
// Driver Code
int main()
{
// Input array
int arr[] = { 1, 3, 1, 7 };
// Size of array
n = sizeof(arr) / sizeof(int);
// Builind the segment-tree
buildMaxTree(0, n - 1, 0, arr);
cout << maxSumSubarray(arr);
return 0;
}
Java
// Java implementation of the above approach
class GFG {
static class pair {
int first, second;
public pair(int first, int second) {
this.first = first;
this.second = second;
}
}
static final int seg_max = 51;
// Array to store segment tree.
// In first we will store the maximum
// of a range
// In second, we will store index of
// that range
static pair[] seg_tree = new pair[seg_max];
// Size of array declared global
// to maintain simplicity in code
static int n;
// Function to build segment tree
static pair buildMaxTree(int l, int r, int i, int arr[])
{
// Base case
if (l == r) {
seg_tree[i] = new pair(arr[l], l);
return seg_tree[i];
}
// Finding the maximum among left and right child
seg_tree[i] = max(buildMaxTree(l, (l + r) / 2, 2 * i + 1, arr),
buildMaxTree((l + r) / 2 + 1, r, 2 * i + 2, arr));
// Returning the maximum to parent
return seg_tree[i];
}
// Function to perform range-max query in segment tree
static pair rangeMax(int l, int r, int arr[],
int i, int sl, int sr)
{
// Base cases
if (sr < l || sl > r)
return new pair(Integer.MIN_VALUE, -1);
if (sl >= l && sr <= r)
return seg_tree[i];
// Finding the maximum among left and right child
return max(rangeMax(l, r, arr, 2 * i + 1, sl, (sl + sr) / 2),
rangeMax(l, r, arr, 2 * i + 2, (sl + sr) / 2 + 1, sr));
}
static pair max(pair f, pair s) {
if (f.first > s.first)
return f;
else
return s;
}
// Function to find maximum sum subarray
static int maxSumSubarray(int arr[], int l, int r)
{
// base case
if (l > r)
return 0;
// range-max query to determine
// largest in the range.
pair a = rangeMax(l, r, arr, 0, 0, n - 1);
// divide the array in two parts
return a.first * (r - a.second + 1) * (a.second - l + 1)
+ maxSumSubarray(arr, l, a.second - 1)
+ maxSumSubarray(arr, a.second + 1, r);
}
// Driver Code
public static void main(String[] args)
{
// Input array
int arr[] = { 1, 3, 1, 7 };
// Size of array
n = arr.length;
// Builind the segment-tree
buildMaxTree(0, n - 1, 0, arr);
System.out.print(maxSumSubarray(arr, 0, n - 1));
}
}
// This code is contributed by 29AjayKumar
C#
// C# implementation of the above approach
using System;
class GFG {
class pair {
public int first, second;
public pair(int first, int second) {
this.first = first;
this.second = second;
}
}
static readonly int seg_max = 51;
// Array to store segment tree.
// In first we will store the maximum
// of a range
// In second, we will store index of
// that range
static pair[] seg_tree = new pair[seg_max];
// Size of array declared global
// to maintain simplicity in code
static int n;
// Function to build segment tree
static pair buildMaxTree(int l, int r, int i, int []arr)
{
// Base case
if (l == r) {
seg_tree[i] = new pair(arr[l], l);
return seg_tree[i];
}
// Finding the maximum among left and right child
seg_tree[i] = max(buildMaxTree(l, (l + r) / 2, 2 * i + 1, arr),
buildMaxTree((l + r) / 2 + 1, r, 2 * i + 2, arr));
// Returning the maximum to parent
return seg_tree[i];
}
// Function to perform range-max query in segment tree
static pair rangeMax(int l, int r, int []arr,
int i, int sl, int sr)
{
// Base cases
if (sr < l || sl > r)
return new pair(int.MinValue, -1);
if (sl >= l && sr <= r)
return seg_tree[i];
// Finding the maximum among left and right child
return max(rangeMax(l, r, arr, 2 * i + 1, sl, (sl + sr) / 2),
rangeMax(l, r, arr, 2 * i + 2, (sl + sr) / 2 + 1, sr));
}
static pair max(pair f, pair s) {
if (f.first > s.first)
return f;
else
return s;
}
// Function to find maximum sum subarray
static int maxSumSubarray(int []arr, int l, int r)
{
// base case
if (l > r)
return 0;
// range-max query to determine
// largest in the range.
pair a = rangeMax(l, r, arr, 0, 0, n - 1);
// divide the array in two parts
return a.first * (r - a.second + 1) * (a.second - l + 1)
+ maxSumSubarray(arr, l, a.second - 1)
+ maxSumSubarray(arr, a.second + 1, r);
}
// Driver Code
public static void Main(String[] args)
{
// Input array
int []arr = { 1, 3, 1, 7 };
// Size of array
n = arr.Length;
// Builind the segment-tree
buildMaxTree(0, n - 1, 0, arr);
Console.Write(maxSumSubarray(arr, 0, n - 1));
}
}
// This code is contributed by PrinciRaj1992
Javascript
输出:
42
时间复杂度:O(Nlog(N))
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。