Given an array of integers arr[] and an integer k. You may freely shuffle the elements of the array and shuffling is not counted as an operation.
Find the minimum of number of below operations to make the array palindrome.
- Select any element in the array and add k to it (i.e., perform arr[i] = arr[i] + k ).
Note:Â If it is not possible to make a palindromic sequence, return -1.
Examples:
Input: arr[] = [1, 4, 5], k = 2
Output: 2
Explanation: Perform operation on arr[0] and after that array is [3, 4, 5], Perform operation on arr[0] again after that array is [5, 4, 5]. Which is palindromic sequence, so minimum operations required is 2.Input: arr[] = [10, 9, 10], k = 1
Output: 0
Explanation: It is already a palindromic sequence, hence no operation is required.
Table of Content
[Naive Approach] Permutation Check - O(n! × n) Time and O(n) Space
Generate every permutation of array. For each permutation, check if it can be made palindrome by adding multiples of k to elements. Track minimum operations.
- Sort array to generate all permutations
- For each permutation, create copy and set operations to zero
- Use two pointers from both ends
- If left value is greater, difference must be divisible by k
- If difference divisible, add quotient to operations
- If not divisible, permutation invalid
- If left value is smaller, apply same logic
- Update minimum operations if valid
- Return minimum or -1
#include <iostream>
#include <vector>
using namespace std;
int minOperations(vector<int> &arr, int k)
{
int n = arr.size();
// Single element is already a palindrome
if (n <= 1)
return 0;
// Sort so that next_permutation
// generates all permutations
sort(arr.begin(), arr.end());
int minOps = INT_MAX;
// Try every possible arrangement of the array
do
{
vector<int> temp = arr;
int operations = 0;
bool possible = true;
int left = 0, right = n - 1;
// Make both ends equal
while (left < right)
{
if (temp[left] > temp[right])
{
int diff = temp[left] - temp[right];
if (diff % k == 0)
{
operations += diff / k;
temp[right] = temp[left];
}
else
{
possible = false;
break;
}
}
else if (temp[left] < temp[right])
{
int diff = temp[right] - temp[left];
if (diff % k == 0)
{
operations += diff / k;
temp[left] = temp[right];
}
else
{
possible = false;
break;
}
}
left++;
right--;
}
// Update answer
if (possible)
minOps = min(minOps, operations);
} while (next_permutation(arr.begin(), arr.end()));
return (minOps == INT_MAX) ? -1 : minOps;
}
int main()
{
vector<int> arr = {1, 4, 5};
int k = 2;
cout << minOperations(arr, k) << endl;
return 0;
}
class GFG {
static int minOperations(int[] arr, int k) {
int n = arr.length;
// Single element is already a palindrome
if (n <= 1)
return 0;
// Sort so that permutations are generated
Arrays.sort(arr);
int minOps = Integer.MAX_VALUE;
// Try every possible arrangement of the array
do {
int[] temp = arr.clone();
int operations = 0;
boolean possible = true;
int left = 0, right = n - 1;
// Make both ends equal
while (left < right) {
if (temp[left] > temp[right]) {
int diff = temp[left] - temp[right];
if (diff % k == 0) {
operations += diff / k;
temp[right] = temp[left];
} else {
possible = false;
break;
}
} else if (temp[left] < temp[right]) {
int diff = temp[right] - temp[left];
if (diff % k == 0) {
operations += diff / k;
temp[left] = temp[right];
} else {
possible = false;
break;
}
}
left++;
right--;
}
// Update answer
if (possible)
minOps = Math.min(minOps, operations);
} while (nextPermutation(arr));
return (minOps == Integer.MAX_VALUE) ? -1 : minOps;
}
// Helper function to generate next permutation
static boolean nextPermutation(int[] arr) {
int i = arr.length - 2;
while (i >= 0 && arr[i] >= arr[i + 1]) {
i--;
}
if (i < 0)
return false;
int j = arr.length - 1;
while (arr[j] <= arr[i]) {
j--;
}
// Swap
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
// Reverse suffix
int left = i + 1, right = arr.length - 1;
while (left < right) {
temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++;
right--;
}
return true;
}
public static void main(String[] args) {
int[] arr = {1, 4, 5};
int k = 2;
System.out.println(minOperations(arr, k));
}
}
from itertools import permutations
def minOperations(arr, k):
n = len(arr)
# Single element is already a palindrome
if n <= 1:
return 0
minOps = float('inf')
# Try every possible arrangement of the array
for perm in set(permutations(arr)):
temp = list(perm)
operations = 0
possible = True
left, right = 0, n - 1
# Make both ends equal
while left < right:
if temp[left] > temp[right]:
diff = temp[left] - temp[right]
if diff % k == 0:
operations += diff // k
temp[right] = temp[left]
else:
possible = False
break
elif temp[left] < temp[right]:
diff = temp[right] - temp[left]
if diff % k == 0:
operations += diff // k
temp[left] = temp[right]
else:
possible = False
break
left += 1
right -= 1
# Update answer
if possible:
minOps = min(minOps, operations)
return -1 if minOps == float('inf') else minOps
if __name__ == "__main__":
arr = [1, 4, 5]
k = 2
print(minOperations(arr, k))
// C# program to find minimum operations to make array palindrome
using System;
using System.Collections.Generic;
using System.Linq;
class GFG {
static int minOperations(int[] arr, int k) {
int n = arr.Length;
// Single element is already a palindrome
if (n <= 1)
return 0;
// Sort so that permutations are generated
Array.Sort(arr);
int minOps = int.MaxValue;
// Try every possible arrangement of the array
do {
int[] temp = (int[])arr.Clone();
int operations = 0;
bool possible = true;
int left = 0, right = n - 1;
// Make both ends equal
while (left < right) {
if (temp[left] > temp[right]) {
int diff = temp[left] - temp[right];
if (diff % k == 0) {
operations += diff / k;
temp[right] = temp[left];
} else {
possible = false;
break;
}
} else if (temp[left] < temp[right]) {
int diff = temp[right] - temp[left];
if (diff % k == 0) {
operations += diff / k;
temp[left] = temp[right];
} else {
possible = false;
break;
}
}
left++;
right--;
}
// Update answer
if (possible)
minOps = Math.Min(minOps, operations);
} while (NextPermutation(arr));
return (minOps == int.MaxValue) ? -1 : minOps;
}
// Helper function to generate next permutation
static bool NextPermutation(int[] arr) {
int i = arr.Length - 2;
while (i >= 0 && arr[i] >= arr[i + 1]) {
i--;
}
if (i < 0)
return false;
int j = arr.Length - 1;
while (arr[j] <= arr[i]) {
j--;
}
// Swap
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
// Reverse suffix
Array.Reverse(arr, i + 1, arr.Length - i - 1);
return true;
}
static void Main(string[] args) {
int[] arr = {1, 4, 5};
int k = 2;
Console.WriteLine(minOperations(arr, k));
}
}
function minOperations(arr, k) {
const n = arr.length;
// Single element is already a palindrome
if (n <= 1)
return 0;
// Sort so that permutations are generated
arr.sort((a, b) => a - b);
let minOps = Infinity;
// Try every possible arrangement of the array
const permute = (arr, start, callback) => {
if (start === arr.length) {
callback([...arr]);
return;
}
const seen = new Set();
for (let i = start; i < arr.length; i++) {
if (seen.has(arr[i])) continue;
seen.add(arr[i]);
[arr[start], arr[i]] = [arr[i], arr[start]];
permute(arr, start + 1, callback);
[arr[start], arr[i]] = [arr[i], arr[start]];
}
};
permute(arr, 0, (perm) => {
let temp = [...perm];
let operations = 0;
let possible = true;
let left = 0, right = n - 1;
// Make both ends equal
while (left < right) {
if (temp[left] > temp[right]) {
const diff = temp[left] - temp[right];
if (diff % k === 0) {
operations += diff / k;
temp[right] = temp[left];
} else {
possible = false;
break;
}
} else if (temp[left] < temp[right]) {
const diff = temp[right] - temp[left];
if (diff % k === 0) {
operations += diff / k;
temp[left] = temp[right];
} else {
possible = false;
break;
}
}
left++;
right--;
}
// Update answer
if (possible)
minOps = Math.min(minOps, operations);
});
return (minOps === Infinity) ? -1 : minOps;
}
// Driver code
const arr = [1, 4, 5];
const k = 2;
console.log(minOperations(arr, k));
Output
2
[Expected Approach] Remainder Grouping with Median - O(n log n) Time and O(n) Space
Group elements by remainder modulo k. For each group, reduce values to quotients. To minimize operations, pair smallest with largest. For odd-sized group, choose one element to leave unpaired using median approach.
- Group numbers by arr[i] % k, storing arr[i] / k as values
- Sort each group's quotient values
- Pair adjacent elements in sorted order to compute cost
- Count groups with odd size
- If more than one odd group, return -1
- For odd-sized group, try all possible unpaired elements to minimize cost
- Return total operations
#include <iostream>
#include <vector>
using namespace std;
int minOperations(vector<int> &arr, int k)
{
int n = arr.size();
// Store numbers according to their
// remainder when divided by k
map<int, vector<int>> groups;
for (int i = 0; i < n; i++)
{
groups[arr[i] % k].push_back(arr[i] / k);
}
int totalOperations = 0;
int oddGroups = 0;
// Process every remainder group separately
for (auto &entry : groups)
{
vector<int> &values = entry.second;
// Sort quotient values
sort(values.begin(), values.end());
int currentCost = 0;
// Count groups having odd number of elements
oddGroups += (values.size() % 2);
// Pair adjacent elements
for (int i = 1; i < values.size(); i += 2)
{
currentCost += values[i] - values[i - 1];
}
// If group size is odd, try every
// possible unpaired element
if (values.size() % 2)
{
int tempCost = currentCost;
for (int i = values.size() - 2; i >= 1; i -= 2)
{
tempCost += values[i + 1] + values[i - 1] - 2 * values[i];
currentCost = min(currentCost, tempCost);
}
}
totalOperations += currentCost;
}
// More than one odd-sized group
// cannot form a palindrome
if (oddGroups > 1)
return -1;
return totalOperations;
}
int main()
{
vector<int> arr = {1, 4, 5};
int k = 2;
cout << minOperations(arr, k) << endl;
return 0;
}
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
class GFG {
static int minOperations(int[] arr, int k) {
int n = arr.length;
// Store numbers according to their remainder when divided by k
Map<Integer, List<Integer>> groups = new HashMap<>();
for (int i = 0; i < n; i++) {
int rem = arr[i] % k;
int quotient = arr[i] / k;
groups.computeIfAbsent(rem, key -> new ArrayList<>()).add(quotient);
}
int totalOperations = 0;
int oddGroups = 0;
// Process every remainder group separately
for (Map.Entry<Integer, List<Integer>> entry : groups.entrySet()) {
List<Integer> values = entry.getValue();
// Sort quotient values
Collections.sort(values);
int currentCost = 0;
// Count groups having odd number of elements
if (values.size() % 2 == 1)
oddGroups++;
// Pair adjacent elements
for (int i = 1; i < values.size(); i += 2) {
currentCost += values.get(i) - values.get(i - 1);
}
// If group size is odd, try every possible unpaired element
if (values.size() % 2 == 1) {
int tempCost = currentCost;
for (int i = values.size() - 2; i >= 1; i -= 2) {
tempCost += values.get(i + 1) + values.get(i - 1) - 2 * values.get(i);
currentCost = Math.min(currentCost, tempCost);
}
}
totalOperations += currentCost;
}
// More than one odd-sized group cannot form a palindrome
if (oddGroups > 1)
return -1;
return totalOperations;
}
public static void main(String[] args) {
int[] arr = {1, 4, 5};
int k = 2;
System.out.println(minOperations(arr, k));
}
}
from collections import defaultdict
def minOperations(arr, k):
n = len(arr)
# Store numbers according to their remainder when divided by k
groups = defaultdict(list)
for num in arr:
groups[num % k].append(num // k)
totalOperations = 0
oddGroups = 0
# Process every remainder group separately
for rem, values in groups.items():
# Sort quotient values
values.sort()
currentCost = 0
# Count groups having odd number of elements
if len(values) % 2 == 1:
oddGroups += 1
# Pair adjacent elements
for i in range(1, len(values), 2):
currentCost += values[i] - values[i - 1]
# If group size is odd, try every possible unpaired element
if len(values) % 2 == 1:
tempCost = currentCost
for i in range(len(values) - 2, 0, -2):
tempCost += values[i + 1] + values[i - 1] - 2 * values[i]
currentCost = min(currentCost, tempCost)
totalOperations += currentCost
# More than one odd-sized group cannot form a palindrome
if oddGroups > 1:
return -1
return totalOperations
if __name__ == "__main__":
arr = [1, 4, 5]
k = 2
print(minOperations(arr, k))
using System;
using System.Collections.Generic;
using System.Linq;
class GfG {
static int minOperations(int[] arr, int k) {
int n = arr.Length;
// Store numbers according to their remainder when divided by k
Dictionary<int, List<int>> groups = new Dictionary<int, List<int>>();
for (int i = 0; i < n; i++) {
int rem = arr[i] % k;
int quotient = arr[i] / k;
if (!groups.ContainsKey(rem))
groups[rem] = new List<int>();
groups[rem].Add(quotient);
}
int totalOperations = 0;
int oddGroups = 0;
// Process every remainder group separately
foreach (var entry in groups) {
List<int> values = entry.Value;
// Sort quotient values
values.Sort();
int currentCost = 0;
// Count groups having odd number of elements
if (values.Count % 2 == 1)
oddGroups++;
// Pair adjacent elements
for (int i = 1; i < values.Count; i += 2) {
currentCost += values[i] - values[i - 1];
}
// If group size is odd, try every possible unpaired element
if (values.Count % 2 == 1) {
int tempCost = currentCost;
for (int i = values.Count - 2; i >= 1; i -= 2) {
tempCost += values[i + 1] + values[i - 1] - 2 * values[i];
currentCost = Math.Min(currentCost, tempCost);
}
}
totalOperations += currentCost;
}
// More than one odd-sized group cannot form a palindrome
if (oddGroups > 1)
return -1;
return totalOperations;
}
static void Main(string[] args) {
int[] arr = {1, 4, 5};
int k = 2;
Console.WriteLine(minOperations(arr, k));
}
}
function minOperations(arr, k) {
const n = arr.length;
// Store numbers according to their
// remainder when divided by k
let groups = new Map();
for (let num of arr) {
let rem = num % k;
let quotient = Math.floor(num / k);
if (!groups.has(rem))
groups.set(rem, []);
groups.get(rem).push(quotient);
}
let totalOperations = 0;
let oddGroups = 0;
// Process every remainder group separately
for (let [rem, values] of groups) {
// Sort quotient values
values.sort((a, b) => a - b);
let currentCost = 0;
// Count groups having odd number of elements
if (values.length % 2 === 1)
oddGroups++;
// Pair adjacent elements
for (let i = 1; i < values.length; i += 2) {
currentCost += values[i] - values[i - 1];
}
// If group size is odd, try every possible unpaired element
if (values.length % 2 === 1) {
let tempCost = currentCost;
for (let i = values.length - 2; i >= 1; i -= 2) {
tempCost += values[i + 1] + values[i - 1] - 2 * values[i];
currentCost = Math.min(currentCost, tempCost);
}
}
totalOperations += currentCost;
}
// More than one odd-sized group cannot form a palindrome
if (oddGroups > 1)
return -1;
return totalOperations;
}
// Driver code
const arr = [1, 4, 5];
const k = 2;
console.log(minOperations(arr, k));
Output
2