Given an array arr[] of positive integers, return the count of groups of two or three such that the sum of all elements in a group is a multiple of 3.
Examples:
Input: arr[] = {3, 6, 7, 2, 9}
Output: 8
Explanation: Groups are {3,6}, {3,9}, {9,6}, {7,2}, {3,6,9}, {3,7,2}, {7,2,6}, {7,2,9}.
Input: arr[] = {2, 1, 3, 4}
Output: 4
Explanation: Groups are {2,1}, {2,4}, {2,1,3}, {2,4,3}.
Table of Content
[Naive Approach] Using Three Nested Loops - O(n ^ 3) Time and O(1) Space
The idea is to check every possible group of size 2 and 3 from the array. For each pair or triplet, we calculate its sum and verify whether it is divisible by 3. If the condition is satisfied, we increment the count.
#include <bits/stdc++.h>
using namespace std;
// Function to count valid groups
long long findGroups(vector<int> &arr)
{
int n = arr.size();
long long count = 0;
// Check all possible pairs
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
int sum = arr[i] + arr[j];
// If sum is divisible by 3
if (sum % 3 == 0)
{
count++;
}
}
}
// Check all possible triplets
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
for (int k = j + 1; k < n; k++)
{
int sum = arr[i] + arr[j] + arr[k];
// If sum is divisible by 3
if (sum % 3 == 0)
{
count++;
}
}
}
}
return count;
}
int main()
{
vector<int> arr = {3, 6, 7, 2, 9};
cout << findGroups(arr);
return 0;
}
public class GFG {
// Function to count valid groups
static long findGroups(int[] arr)
{
int n = arr.length;
long count = 0;
// Check all possible pairs
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int sum = arr[i] + arr[j];
// If sum is divisible by 3
if (sum % 3 == 0) {
count++;
}
}
}
// Check all possible triplets
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
int sum = arr[i] + arr[j] + arr[k];
// If sum is divisible by 3
if (sum % 3 == 0) {
count++;
}
}
}
}
return count;
}
public static void main(String[] args)
{
int[] arr = { 3, 6, 7, 2, 9 };
System.out.println(findGroups(arr));
}
}
# Function to count valid groups
def findGroups(arr):
n = len(arr)
count = 0
# Check all possible pairs
for i in range(n):
for j in range(i + 1, n):
summ = arr[i] + arr[j]
# If sum is divisible by 3
if summ % 3 == 0:
count += 1
# Check all possible triplets
for i in range(n):
for j in range(i + 1, n):
for k in range(j + 1, n):
summ = arr[i] + arr[j] + arr[k]
# If sum is divisible by 3
if summ % 3 == 0:
count += 1
return count
# Driver Code
if __name__ == "__main__":
arr = [3, 6, 7, 2, 9]
print(findGroups(arr))
using System;
class GFG {
// Function to count valid groups
static long findGroups(int[] arr)
{
int n = arr.Length;
long count = 0;
// Check all possible pairs
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int sum = arr[i] + arr[j];
// If sum is divisible by 3
if (sum % 3 == 0) {
count++;
}
}
}
// Check all possible triplets
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
int sum = arr[i] + arr[j] + arr[k];
// If sum is divisible by 3
if (sum % 3 == 0) {
count++;
}
}
}
}
return count;
}
static void Main()
{
int[] arr = { 3, 6, 7, 2, 9 };
Console.WriteLine(findGroups(arr));
}
}
// Function to count valid groups
function findGroups(arr)
{
let n = arr.length;
let count = 0;
// Check all possible pairs
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
let sum = arr[i] + arr[j];
// If sum is divisible by 3
if (sum % 3 === 0) {
count++;
}
}
}
// Check all possible triplets
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
for (let k = j + 1; k < n; k++) {
let sum = arr[i] + arr[j] + arr[k];
// If sum is divisible by 3
if (sum % 3 === 0) {
count++;
}
}
}
}
return count;
}
// Driver Code
let arr = [ 3, 6, 7, 2, 9 ];
console.log(findGroups(arr));
Output
8
[Expected Approach] Using Combinational Approach - O(n) Time and O(1) Space
The main observation is that a number can leave only three possible remainders when divided by 3: 0, 1, or 2. We count how many numbers belong to each remainder category. Then, using valid remainder combinations whose sum becomes divisible by 3, we directly calculate the number of possible groups using combination formulas.
- Create a frequency array c[3] to count elements having remainder 0, 1, and 2 when divided by 3.
- Traverse the array and store the count of each remainder category in c.
- Count valid groups of size 2. Choose any 2 elements from remainder 0. Choose one element from remainder 1 and one from remainder 2.
- Count valid groups of size 3. All three elements from remainder 0. All three elements from remainder 1. All three elements from remainder 2.
- Count groups containing one element each from remainder 0, 1, and 2.
- Add all valid combinations and return the final count of groups whose sum is divisible by 3.
Consider the following dry run for better understanding : arr = {3, 6, 7, 2, 9}
- Initial Values: n = 5, c[0] = 0, c[1] = 0, c[2] = 0, res = 0
- Count Remainder Frequencies
For i = 0 --> arr[0] = 3 --> 3 % 3 = 0 --> c[0] = 1
For i = 1 --> arr[1] = 6 --> 6 % 3 = 0 --> c[0] = 2
For i = 2 --> arr[2] = 7 --> 7 % 3 = 1 --> c[1] = 1
For i = 3 --> arr[3] = 2 --> 2 % 3 = 2 --> c[2] = 1
For i = 4 --> arr[4] = 9 --> 9 % 3 = 0 --> c[0] = 3 - Groups of Size 2
Case 1: Both elements have remainder 0, res += (3 * (3 - 1)) / 2 --> res += 3 --> res = 3
Case 2: One element with remainder 1 and one element with remainder 2, res += 1 * 1 --> res += 1 --> res = 4 - Groups of Size 3
Case 3: All three elements have remainder 0, res += (3 * 2 * 1) / 6 --> res += 1 --> res = 5
Case 4: All three elements have remainder 1, res += (1 * 0 * -1) / 6 --> res += 0 --> res = 5
Case 5: All three elements have remainder 2, res += (1 * 0 * -1) / 6 --> res += 0 --> res = 5
Case 6: One element each from remainder 0, 1 and 2, res += 3 * 1 * 1 --> res += 3 --> res = 8
Final answer : res = 8
#include <bits/stdc++.h>
using namespace std;
// Function to count all valid groups
long long findGroups(vector<int> &arr)
{
int n = arr.size();
// c[0] -> count of numbers having remainder 0
// c[1] -> count of numbers having remainder 1
// c[2] -> count of numbers having remainder 2
vector<long long> c(3, 0);
// Variable to store final answer
long long res = 0;
// Count frequency of each remainder
for (int i = 0; i < n; i++)
{
c[arr[i] % 3]++;
}
// Groups of size 2
// Case 1:
// Both elements have remainder 0
// Example: (3, 6)
res += (c[0] * (c[0] - 1)) / 2;
// Case 2:
// One element has remainder 1
// and the other has remainder 2
// Example: (7, 2)
res += c[1] * c[2];
// Groups of size 3
// Case 3:
// All three elements have remainder 0
res += (c[0] * (c[0] - 1) * (c[0] - 2)) / 6;
// Case 4:
// All three elements have remainder 1
res += (c[1] * (c[1] - 1) * (c[1] - 2)) / 6;
// Case 5:
// All three elements have remainder 2
res += (c[2] * (c[2] - 1) * (c[2] - 2)) / 6;
// Case 6:
// One element from each remainder group
// i.e. 0, 1 and 2
res += c[0] * c[1] * c[2];
// Return total valid groups
return res;
}
int main()
{
vector<int> arr = {3, 6, 7, 2, 9};
cout << findGroups(arr);
return 0;
}
import java.util.*;
public class GFG {
// Function to count all valid groups
static long findGroups(int[] arr)
{
int n = arr.length;
// c[0] -> count of numbers having remainder 0
// c[1] -> count of numbers having remainder 1
// c[2] -> count of numbers having remainder 2
long[] c = new long[3];
// Variable to store final answer
long res = 0;
// Count frequency of each remainder
for (int i = 0; i < n; i++) {
c[arr[i] % 3]++;
}
// Groups of size 2
// Case 1:
// Both elements have remainder 0
// Example: (3, 6)
res += (c[0] * (c[0] - 1)) / 2;
// Case 2:
// One element has remainder 1
// and the other has remainder 2
// Example: (7, 2)
res += c[1] * c[2];
// Groups of size 3
// Case 3:
// All three elements have remainder 0
res += (c[0] * (c[0] - 1) * (c[0] - 2)) / 6;
// Case 4:
// All three elements have remainder 1
res += (c[1] * (c[1] - 1) * (c[1] - 2)) / 6;
// Case 5:
// All three elements have remainder 2
res += (c[2] * (c[2] - 1) * (c[2] - 2)) / 6;
// Case 6:
// One element from each remainder group
// i.e. 0, 1 and 2
res += c[0] * c[1] * c[2];
// Return total valid groups
return res;
}
public static void main(String[] args)
{
int[] arr = { 3, 6, 7, 2, 9 };
System.out.println(findGroups(arr));
}
}
# Function to count all valid groups
def findGroups(arr):
n = len(arr)
# c[0] -> count of numbers having remainder 0
# c[1] -> count of numbers having remainder 1
# c[2] -> count of numbers having remainder 2
c = [0] * 3
# Variable to store final answer
res = 0
# Count frequency of each remainder
for i in range(n):
c[arr[i] % 3] += 1
# Groups of size 2
# Case 1:
# Both elements have remainder 0
# Example: (3, 6)
res += (c[0] * (c[0] - 1)) // 2
# Case 2:
# One element has remainder 1
# and the other has remainder 2
# Example: (7, 2)
res += c[1] * c[2]
# Groups of size 3
# Case 3:
# All three elements have remainder 0
res += (c[0] * (c[0] - 1) * (c[0] - 2)) // 6
# Case 4:
# All three elements have remainder 1
res += (c[1] * (c[1] - 1) * (c[1] - 2)) // 6
# Case 5:
# All three elements have remainder 2
res += (c[2] * (c[2] - 1) * (c[2] - 2)) // 6
# Case 6:
# One element from each remainder group
# i.e. 0, 1 and 2
res += c[0] * c[1] * c[2]
# Return total valid groups
return res
# Driver Code
if __name__ == "__main__":
arr = [3, 6, 7, 2, 9]
print(findGroups(arr))
using System;
class GFG {
// Function to count all valid groups
static long findGroups(int[] arr)
{
int n = arr.Length;
// c[0] -> count of numbers having remainder 0
// c[1] -> count of numbers having remainder 1
// c[2] -> count of numbers having remainder 2
long[] c = new long[3];
// Variable to store final answer
long res = 0;
// Count frequency of each remainder
for (int i = 0; i < n; i++) {
c[arr[i] % 3]++;
}
// Groups of size 2
// Case 1:
// Both elements have remainder 0
// Example: (3, 6)
res += (c[0] * (c[0] - 1)) / 2;
// Case 2:
// One element has remainder 1
// and the other has remainder 2
// Example: (7, 2)
res += c[1] * c[2];
// Groups of size 3
// Case 3:
// All three elements have remainder 0
res += (c[0] * (c[0] - 1) * (c[0] - 2)) / 6;
// Case 4:
// All three elements have remainder 1
res += (c[1] * (c[1] - 1) * (c[1] - 2)) / 6;
// Case 5:
// All three elements have remainder 2
res += (c[2] * (c[2] - 1) * (c[2] - 2)) / 6;
// Case 6:
// One element from each remainder group
// i.e. 0, 1 and 2
res += c[0] * c[1] * c[2];
// Return total valid groups
return res;
}
static void Main()
{
int[] arr = { 3, 6, 7, 2, 9 };
Console.WriteLine(findGroups(arr));
}
}
// Function to count all valid groups
function findGroups(arr)
{
let n = arr.length;
// c[0] -> count of numbers having remainder 0
// c[1] -> count of numbers having remainder 1
// c[2] -> count of numbers having remainder 2
let c = [ 0, 0, 0 ];
// Variable to store final answer
let res = 0;
// Count frequency of each remainder
for (let i = 0; i < n; i++) {
c[arr[i] % 3]++;
}
// Groups of size 2
// Case 1:
// Both elements have remainder 0
// Example: (3, 6)
res += (c[0] * (c[0] - 1)) / 2;
// Case 2:
// One element has remainder 1
// and the other has remainder 2
// Example: (7, 2)
res += c[1] * c[2];
// Groups of size 3
// Case 3:
// All three elements have remainder 0
res += (c[0] * (c[0] - 1) * (c[0] - 2)) / 6;
// Case 4:
// All three elements have remainder 1
res += (c[1] * (c[1] - 1) * (c[1] - 2)) / 6;
// Case 5:
// All three elements have remainder 2
res += (c[2] * (c[2] - 1) * (c[2] - 2)) / 6;
// Case 6:
// One element from each remainder group
// i.e. 0, 1 and 2
res += c[0] * c[1] * c[2];
// Return total valid groups
return res;
}
// Driver Code
let arr = [ 3, 6, 7, 2, 9 ];
console.log(findGroups(arr));
Output
8