给定一个大小为N的整数数组arr 。任务是找到最大拆分数,以使每个拆分的和都可被3整除。并非所有分割都可以被3整除,而是要最大化被3可以整除的分割的数量。
例子:
Input: arr [] = [2, 36, 1, 9, 2, 0, 1, 8, 1]
Output: 4
Explanation
The array can be splited into 4 parts:
[2, 36, 1] Sum = 39
[9] Sum = 9
[2, 0, 1] Sum = 3
[8, 1] Sum = 9
All splits are divisible by 3 and there cannot be more than 4 splits which are divisible by 3.
Input: arr [] = [40, 40, 40, 5]
Output: 1
Explanation:
Array can be splits into only two parts
[40, 40] Sum = 80.
[40, 5] Sum = 45.
The sum of the second split is divisible by 3 hence only one split is divisible by 3 so the output is 1.
方法
可以很容易地得出的一个观察结果是,如果我们将每个元素的模数乘以3,则使用数组会更容易,因为它不会对拆分产生任何影响。现在可以使用动态编程解决问题。
- 令dp [i]表示在位置i处的最大分割数。
- 然后计算模3的前缀和。
- 因此,如果某段的总和可被3整除,并且其左右前缀总和也将相同。
- 要注意的另一件事是,模3的前缀和可以为0,1或2。因此,如果当前的前缀和以3为模,则选择1作为最右边的具有前缀和为1的索引。
dp[i] = max( dp[right most index from 0 to i with prefix sum same as i] + 1, dp[i-1])
dp[i-1] means we are not considering i as right pointer of some segment .
dp[right most index from 0 to i with prefix sum same as i]+1 means i is a right pointer of the segment and total number of splits will be the total number of splits at the left pointer of the segment + 1 (for this segment) .
下面是上述方法的实现:
C++
// C++ program for the above approach
#include
using namespace std;
int calculate_maximum_splits(int arr[], int N)
{
// Prefix array storing right most
// index with prefix sums 0, 1, 2
int pre[] = { 0, -1, -1 };
// dp array
int dp[N];
memset(dp, 0, sizeof(dp));
// Current prefix sum
int C = 0;
for(int i = 0; i < N; i++)
{
C = C + arr[i];
// Calculating the prefix sum modulo 3
C = C % 3;
// We dont have a left pointer
// with prefix sum C
if (pre[C] == -1)
{
dp[i] = dp[i - 1];
// We cannot consider i as
// a right pointer of any segment
}
else
{
// We have a left pointer
// pre[C] with prefix sum C
dp[i] = max(dp[i - 1], dp[pre[C]] + 1);
}
// i is the rightmost index of
// prefix sum C
pre[C] = i;
}
return dp[N - 1];
}
// Driver code
int main()
{
int arr[] = { 2, 36, 1, 9, 2, 0, 1, 8, 1 };
int N = sizeof(arr) / sizeof(arr[0]);
cout << (calculate_maximum_splits(arr, N));
}
// This code is contributed by chitranayal
Java
// Java implementation of the above appraoch
import java.util.*;
class GFG{
static int calculate_maximum_splits(int arr[],
int N)
{
// Prefix array storing right most
// index with prefix sums 0, 1, 2
int pre[] = { 0, -1, -1 };
// dp array
int[] dp = new int[N];
Arrays.fill(dp, 0);
// Current prefix sum
int C = 0;
for(int i = 0; i < N; i++)
{
C = C + arr[i];
// Calculating the prefix sum modulo 3
C = C % 3;
// We dont have a left pointer
// with prefix sum C
if (pre[C] == -1)
{
if(1 <= i)
dp[i] = dp[i - 1];
// We cannot consider i as
// a right pointer of any segment
}
else
{
// We have a left pointer
// pre[C] with prefix sum C
dp[i] = Math.max(dp[i - 1],
dp[pre[C]] + 1);
}
// i is the rightmost index of
// prefix sum C
pre[C] = i;
}
return dp[N - 1];
}
// Driver code
public static void main(String[] args)
{
int arr[] = { 2, 36, 1, 9, 2, 0, 1, 8, 1 };
int N = arr.length;
System.out.println(calculate_maximum_splits(arr, N));
}
}
// This code is contributed by offbeat
Python3
# Python3 program for above approach
def calculate_maximum_splits(arr, N):
# prefix array storing right most
# index with prefix sums 0, 1, 2
pre =[0, -1, -1]
# dp array
dp =[0 for i in range(N)]
# current prefix sum
C = 0
for i in range(N):
C = C + arr[i]
# Calculating the prefix sum modulo 3
C = C % 3
# We dont have a left pointer
# with prefix sum C
if pre[C]==-1:
dp[i]= dp[i-1]
# We cannot consider i as
# a right pointer of any segment
else:
# We have a left pointer
# pre[C] with prefix sum C
dp[i]= max(dp[i-1], dp[pre[C]]+1)
# i is the rightmost index of
# prefix sum C
pre[C]= i
return dp[N-1]
# Driver code
arr = [2, 36, 1, 9, 2, 0, 1, 8, 1]
N = len(arr)
print(calculate_maximum_splits(arr, N))
C#
// C# implementation of the above appraoch
using System;
class GFG{
static int calculate_maximum_splits(int []arr,
int N)
{
// Prefix array storing right most
// index with prefix sums 0, 1, 2
int []pre = { 0, -1, -1 };
// dp array
int[] dp = new int[N];
for(int i = 0; i < N; i++)
{
dp[i] = 0;
}
// Current prefix sum
int C = 0;
for(int i = 0; i < N; i++)
{
C = C + arr[i];
// Calculating the prefix sum modulo 3
C = C % 3;
// We dont have a left pointer
// with prefix sum C
if (pre[C] == -1)
{
if(1 <= i)
dp[i] = dp[i - 1];
// We cannot consider i as
// a right pointer of any segment
}
else
{
// We have a left pointer
// pre[C] with prefix sum C
dp[i] = Math.Max(dp[i - 1],
dp[pre[C]] + 1);
}
// i is the rightmost index of
// prefix sum C
pre[C] = i;
}
return dp[N - 1];
}
// Driver code
public static void Main(string[] args)
{
int []arr = { 2, 36, 1, 9, 2, 0, 1, 8, 1 };
int N = arr.Length;
Console.Write(calculate_maximum_splits(arr, N));
}
}
// This code is contributed by rutvik_56
Javascript
4
时间复杂度: O(N)
辅助空间: O(N)