给定一个大小为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 approach
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 approach
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)
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。