Given an array arr[]. Initially, you have another array containing only 0s. In one operation, you may either:
- Choose any one element and increase its value by 1, or
- Double the values of all elements in the array simultaneously.
Find the minimum number of operations required to transform the initial all-zero array into the given array arr[].
Examples:Â
Input: arr[] = [16, 16, 16]
Output: 7
Explanation: First, increase each element to make the array [1, 1, 1] (3 steps).
Then, multiply the whole array by 2 four times:
[1,1,1] -> [2,2,2] -> [4,4,4] -> [8,8,8] -> [16,16,16]
Total steps = 3 + 4 = 7.Input: arr[] = [2, 3]
Output: 4
Explanation: Start from [0, 0].
Increase both elements to get [1, 1] (2 steps)
Multiply once: [2, 2] (1 step)
Increase second element once: [2, 3] (1 step)
Total steps = 2 + 1 + 1 = 4.
Table of Content
[Naive Approach] BFS State Exploration - O(States × n) Time and O(States × n) Space
Start from all zeros array. Use BFS to explore all reachable states by applying two operations: increment one element or double all elements. First time target array is reached gives minimum operations.
#include <iostream>
#include <vector>
#include <queue>
#include <set>
using namespace std;
int countMinOperations(vector<int>& arr) {
int n = arr.size();
vector<int> start(n, 0);
queue<pair<vector<int>, int>> q;
set<vector<int>> visited;
q.push({start, 0});
visited.insert(start);
while (!q.empty()) {
auto [curr, ops] = q.front();
q.pop();
if (curr == arr) {
return ops;
}
// Try incrementing each individual element by 1
for (int i = 0; i < n; i++) {
vector<int> next = curr;
next[i]++;
bool valid = true;
for (int j = 0; j < n; j++) {
if (next[j] > arr[j]) {
valid = false;
break;
}
}
if (valid && !visited.count(next)) {
visited.insert(next);
q.push({next, ops + 1});
}
}
// Try doubling all elements in the array simultaneously
vector<int> dbl = curr;
for (int& x : dbl) {
x *= 2;
}
bool valid = true;
for (int i = 0; i < n; i++) {
if (dbl[i] > arr[i]) {
valid = false;
break;
}
}
if (valid && !visited.count(dbl)) {
visited.insert(dbl);
q.push({dbl, ops + 1});
}
}
return -1;
}
int main() {
vector<int> arr = {2, 3};
cout << countMinOperations(arr) << endl;
return 0;
}
import java.util.Queue;
import java.util.Set;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.Arrays;
public class GFG {
// Helper class to store the array state
// and the operations count together
static class State {
int[] arr;
int ops;
State(int[] arr, int ops) {
this.arr = arr;
this.ops = ops;
}
}
public static int countMinOperations(int[] arr) {
int n = arr.length;
int[] start = new int[n];
Queue<State> q = new LinkedList<>();
// Track visited states cleanly as
// unique Strings instead of ArrayLists
Set<String> visited = new HashSet<>();
q.add(new State(start, 0));
visited.add(Arrays.toString(start));
while (!q.isEmpty()) {
State currState = q.poll();
int[] curr = currState.arr;
int ops = currState.ops;
if (Arrays.equals(curr, arr)) {
return ops;
}
// Try incrementing each individual element by 1
for (int i = 0; i < n; i++) {
int[] next = curr.clone();
next[i]++;
boolean valid = true;
for (int j = 0; j < n; j++) {
if (next[j] > arr[j]) {
valid = false;
break;
}
}
String nextStr = Arrays.toString(next);
if (valid && !visited.contains(nextStr)) {
visited.add(nextStr);
q.add(new State(next, ops + 1));
}
}
// Try doubling all elements in the array simultaneously
int[] dbl = curr.clone();
for (int i = 0; i < n; i++) {
dbl[i] *= 2;
}
boolean valid = true;
for (int i = 0; i < n; i++) {
if (dbl[i] > arr[i]) {
valid = false;
break;
}
}
String dblStr = Arrays.toString(dbl);
if (valid && !visited.contains(dblStr)) {
visited.add(dblStr);
q.add(new State(dbl, ops + 1));
}
}
return -1;
}
public static void main(String[] args) {
int[] arr = {2, 3};
System.out.println(countMinOperations(arr));
}
}
from collections import deque
# Helper class to store the array state and the operations count together
class State:
def __init__(self, arr, ops):
self.arr = arr
self.ops = ops
# Helper method to convert primitive int[] array to list
def to_list(arr):
return arr.copy()
def countMinOperations(arr):
n = len(arr)
start = [0] * n
q = deque()
visited = set()
q.append(State(start, 0))
visited.add(tuple(to_list(start)))
while q:
currState = q.popleft()
curr = currState.arr
ops = currState.ops
if curr == arr:
return ops
# Try incrementing each individual element by 1
for i in range(n):
next = curr.copy()
next[i] += 1
valid = True
for j in range(n):
if next[j] > arr[j]:
valid = False
break
if valid and tuple(next) not in visited:
visited.add(tuple(next))
q.append(State(next, ops + 1))
# Try doubling all elements in the array simultaneously
dbl = [x * 2 for x in curr]
valid = True
for i in range(n):
if dbl[i] > arr[i]:
valid = False
break
if valid and tuple(dbl) not in visited:
visited.add(tuple(dbl))
q.append(State(dbl, ops + 1))
return -1
if __name__ == "__main__":
arr = [2, 3]
print(countMinOperations(arr))
using System;
using System.Collections.Generic;
public class GFG {
// Helper class to store the array state
// and the operations count together
public class State {
public int[] arr;
public int ops;
public State(int[] arr, int ops) {
this.arr = arr;
this.ops = ops;
}
}
public static int countMinOperations(int[] arr) {
int n = arr.Length;
int[] start = new int[n];
Queue<State> q = new Queue<State>();
// Track visited states cleanly as
// unique Strings instead of ArrayLists
HashSet<string> visited = new HashSet<string>();
q.Enqueue(new State(start, 0));
visited.Add(string.Join(", ", start));
while (q.Count > 0) {
State currState = q.Dequeue();
int[] curr = currState.arr;
int ops = currState.ops;
if (arraysEqual(curr, arr)) {
return ops;
}
// Try incrementing each individual element by 1
for (int i = 0; i < n; i++) {
int[] next = (int[])curr.Clone();
next[i]++;
bool valid = true;
for (int j = 0; j < n; j++) {
if (next[j] > arr[j]) {
valid = false;
break;
}
}
string nextStr = string.Join(", ", next);
if (valid &&!visited.Contains(nextStr)) {
visited.Add(nextStr);
q.Enqueue(new State(next, ops + 1));
}
}
// Try doubling all elements in the array simultaneously
int[] dbl = (int[])curr.Clone();
for (int i = 0; i < n; i++) {
dbl[i] *= 2;
}
bool valid2 = true;
for (int i = 0; i < n; i++) {
if (dbl[i] > arr[i]) {
valid2 = false;
break;
}
}
string dblStr = string.Join(", ", dbl);
if (valid2 &&!visited.Contains(dblStr)) {
visited.Add(dblStr);
q.Enqueue(new State(dbl, ops + 1));
}
}
return -1;
}
private static bool arraysEqual(int[] a, int[] b) {
if (ReferenceEquals(a, b))
return true;
if (a == null || b == null)
return false;
if (a.Length!= b.Length)
return false;
for (int i = 0; i < a.Length; i++) {
if (a[i]!= b[i])
return false;
}
return true;
}
public static void Main(string[] args) {
int[] arr = {2, 3};
Console.WriteLine(countMinOperations(arr));
}
}
// Helper function to store the array state
// and the operations count together
function createState(arr, ops) {
return { arr, ops };
}
function countMinOperations(arr) {
let n = arr.length;
let start = new Array(n).fill(0);
let q = [];
let visited = new Set();
q.push(createState(start, 0));
visited.add(start.toString());
while (q.length > 0) {
let currState = q.shift();
let curr = currState.arr;
let ops = currState.ops;
// Check if current array matches the target array
if (JSON.stringify(curr) === JSON.stringify(arr)) {
return ops;
}
// Try incrementing each individual element by 1
for (let i = 0; i < n; i++) {
let next = [...curr];
next[i]++;
let valid = true;
for (let j = 0; j < n; j++) {
if (next[j] > arr[j]) {
valid = false;
break;
}
}
let nextStr = next.toString();
if (valid && !visited.has(nextStr)) {
visited.add(nextStr);
q.push(createState(next, ops + 1));
}
}
// Try doubling all elements in the array simultaneously
let dbl = [...curr];
for (let i = 0; i < n; i++) {
dbl[i] *= 2;
}
let valid = true;
for (let i = 0; i < n; i++) {
if (dbl[i] > arr[i]) {
valid = false;
break;
}
}
let dblStr = dbl.toString();
if (valid && !visited.has(dblStr)) {
visited.add(dblStr);
q.push(createState(dbl, ops + 1));
}
}
return -1;
}
// Driver code
let arr = [2, 3];
console.log(countMinOperations(arr));
Output
4
[Expected Approach] Reverse Greedy - O(n × log m) Time and O(n) Space
Work backwards from target array to all zeros. Odd numbers must have come from increment operation, so decrement them. When all numbers become even, undo one doubling by dividing all by 2.
- Copy arr to nums array
- Initialize operations = 0
- While not all zeros
- If any number is odd, decrement it and increment operations
- If all numbers are even and at least one non-zero, divide all by 2 and increment operations
- Return operations
#include <iostream>
#include <vector>
using namespace std;
int countMinOperations(vector<int>& arr) {
vector<int> nums = arr;
int ops = 0;
while (true) {
bool allZero = true;
for (int val : nums) {
if (val != 0) {
allZero = false;
break;
}
}
if (allZero) {
return ops;
}
// Undo increment operations for any odd numbers
for (int i = 0; i < nums.size(); i++) {
if (nums[i] & 1) {
nums[i]--;
ops++;
}
}
bool hasNonZero = false;
for (int val : nums) {
if (val > 0) {
hasNonZero = true;
break;
}
}
// Undo one simultaneous doubling operation
// by dividing all elements by 2
if (hasNonZero) {
for (int& val : nums) {
val /= 2;
}
ops++;
}
}
}
int main() {
vector<int> arr = {2, 3};
cout << countMinOperations(arr) << endl;
return 0;
}
class GFG {
public static int countMinOperations(int[] arr) {
int[] nums = arr.clone();
int ops = 0;
while (true) {
boolean allZero = true;
for (int val : nums) {
if (val != 0) {
allZero = false;
break;
}
}
if (allZero) {
return ops;
}
// Undo increment operations for any odd numbers
for (int i = 0; i < nums.length; i++) {
if ((nums[i] & 1) != 0) {
nums[i]--;
ops++;
}
}
boolean hasNonZero = false;
for (int val : nums) {
if (val > 0) {
hasNonZero = true;
break;
}
}
// Undo one simultaneous doubling operation
// by dividing all elements by 2
if (hasNonZero) {
for (int i = 0; i < nums.length; i++) {
nums[i] /= 2;
}
ops++;
}
}
}
public static void main(String[] args) {
int[] arr = {2, 3};
System.out.println(countMinOperations(arr));
}
}
def countMinOperations(arr):
nums = arr.copy()
ops = 0
while True:
all_zero = all(val == 0 for val in nums)
if all_zero:
return ops
# Undo increment operations for any odd numbers
for i in range(len(nums)):
if nums[i] % 2!= 0:
nums[i] -= 1
ops += 1
has_non_zero = any(val > 0 for val in nums)
# Undo one simultaneous doubling operation
# by dividing all elements by 2
if has_non_zero:
for i in range(len(nums)):
nums[i] //= 2
ops += 1
if __name__ == '__main__':
arr = [2, 3]
print(countMinOperations(arr))
using System;
class GFG {
public static int countMinOperations(int[] arr) {
int[] nums = (int[])arr.Clone();
int ops = 0;
while (true) {
bool allZero = true;
foreach (int val in nums) {
if (val!= 0) {
allZero = false;
break;
}
}
if (allZero) {
return ops;
}
// Undo increment operations for any odd numbers
for (int i = 0; i < nums.Length; i++) {
if ((nums[i] & 1)!= 0) {
nums[i]--;
ops++;
}
}
bool hasNonZero = false;
foreach (int val in nums) {
if (val > 0) {
hasNonZero = true;
break;
}
}
// Undo one simultaneous doubling operation
// by dividing all elements by 2
if (hasNonZero) {
for (int i = 0; i < nums.Length; i++) {
nums[i] /= 2;
}
ops++;
}
}
}
public static void Main(string[] args) {
int[] arr = {2, 3};
Console.WriteLine(countMinOperations(arr));
}
}
function countMinOperations(arr) {
let nums = [...arr];
let ops = 0;
while (true) {
let allZero = nums.every(val => val === 0);
if (allZero) {
return ops;
}
// Undo increment operations for any odd numbers
for (let i = 0; i < nums.length; i++) {
if (nums[i] % 2!== 0) {
nums[i]--;
ops++;
}
}
let hasNonZero = nums.some(val => val > 0);
// Undo one simultaneous doubling operation
// by dividing all elements by 2
if (hasNonZero) {
for (let i = 0; i < nums.length; i++) {
nums[i] = Math.floor(nums[i] / 2);
}
ops++;
}
}
}
// Driver code
let arr = [2, 3];
console.log(countMinOperations(arr));
Output
4
[Optimal Approach] Bit Manipulation - O(n × log m) Time and O(1) Space
Each set bit in binary representation corresponds to an increment operation. Number of doubling operations equals maximum bit length minus one. Total operations = total set bits + (max bit length - 1).
Initialize incs = 0, maxLen = 0
For each val in array
- Check if the lowest bit is set (val & 1) to count and add to incs
- Compute bit length len by right shifting until val becomes 0
- Update maxLen with maximum bit length found
Compute dbls = max(0, maxLen - 1)
Return incs + dbls
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int countMinOperations(vector<int>& arr) {
// Tracks total increment operations (set bits)
int incs = 0;
// Tracks the maximum bit length found
int maxLen = 0;
for (int val : arr) {
int len = 0;
while (val > 0) {
// An odd number (lowest bit set) implies an increment operation
if (val & 1) {
incs++;
}
len++;
// Shift right to inspect the next bit
val >>= 1;
}
maxLen = max(maxLen, len);
}
// Total doubling operations equals (max bit length - 1)
int dbls = max(0, maxLen - 1);
return incs + dbls;
}
int main() {
vector<int> arr = {2, 3};
cout << countMinOperations(arr) << endl;
return 0;
}
import java.util.Arrays;
class GFG {
public static int countMinOperations(int[] arr) {
// Tracks total increment operations (set bits)
int incs = 0;
// Tracks the maximum bit length found
int maxLen = 0;
for (int val : arr) {
int len = 0;
while (val > 0) {
// An odd number (lowest bit set) implies an increment operation
if ((val & 1)!= 0) {
incs++;
}
len++;
// Shift right to inspect the next bit
val >>= 1;
}
maxLen = Math.max(maxLen, len);
}
// Total doubling operations equals (max bit length - 1)
int dbls = Math.max(0, maxLen - 1);
return incs + dbls;
}
public static void main(String[] args) {
int[] arr = {2, 3};
System.out.println(countMinOperations(arr));
}
}
def countMinOperations(arr):
# Tracks total increment operations (set bits)
incs = 0
# Tracks the maximum bit length found
maxLen = 0
for val in arr:
len = 0
while val > 0:
# An odd number (lowest bit set) implies an increment operation
if (val & 1)!= 0:
incs += 1
len += 1
# Shift right to inspect the next bit
val >>= 1
maxLen = max(maxLen, len)
# Total doubling operations equals (max bit length - 1)
dbls = max(0, maxLen - 1)
return incs + dbls
if __name__ == "__main__":
arr = [2, 3]
print(countMinOperations(arr))
using System;
class GFG {
public static int countMinOperations(int[] arr) {
// Tracks total increment operations (set bits)
int incs = 0;
// Tracks the maximum bit length found
int maxLen = 0;
foreach (int val in arr) {
int len = 0;
// Create a modifiable copy of the foreach variable
int temp = val;
while (temp > 0) {
// An odd number (lowest bit set) implies an increment operation
if ((temp & 1) != 0) {
incs++;
}
len++;
// Shift right to inspect the next bit
temp >>= 1;
}
maxLen = Math.Max(maxLen, len);
}
// Total doubling operations equals (max bit length - 1)
int dbls = Math.Max(0, maxLen - 1);
return incs + dbls;
}
public static void Main(string[] args) {
int[] arr = { 2, 3 };
Console.WriteLine(countMinOperations(arr));
}
}
function countMinOperations(arr)
{
// Tracks total increment operations (set bits)
let incs = 0;
// Tracks the maximum bit length found
let maxLen = 0;
for (let val of arr) {
let len = 0;
while (val > 0) {
// An odd number (lowest bit set) implies an
// increment operation
if ((val & 1) != 0) {
incs++;
}
len++;
// Shift right to inspect the next bit
val >>= 1;
}
maxLen = Math.max(maxLen, len);
}
// Total doubling operations equals (max bit length - 1)
let dbls = Math.max(0, maxLen - 1);
return incs + dbls;
}
// Driver code
let arr = [2, 3];
console.log(countMinOperations(arr));
Output
4