Topics
We are given a boolean array of length . We are allowed to perform exactly one operation: choose a range (where ) and flip all the values within this range. Flipping a value means changing it to (i.e., 0 becomes 1, and 1 becomes 0). The goal is to maximize the total number of ones in the array after performing exactly one flip operation.
Idea
Approach 1: Brute Force
The most straightforward approach is to try all possible valid ranges for the flip operation. For each range, we perform the flip, count the number of ones in the resulting array, and keep track of the maximum count encountered so far.
To efficiently count the number of ones and zeros in ranges, we can utilize the prefix sum array. Let’s define a prefix sum array where for , and . Then, stores the count of ones in the prefix .
Using the prefix sum array, we can quickly calculate the number of ones within any range in the original array. The number of ones in the range (inclusive, 0-based indexing) is given by . The number of zeros in the range is simply the length of the range minus the number of ones, which is . After flipping, all zeros become ones and all ones become zeros in this range. So, in the range after flipping, we will have ones and zeros.
Outside the range , the values remain unchanged. The number of ones to the left of index (indices ) is . The number of ones to the right of index (indices ) is .
Total ones after flip:
We can iterate through all possible start indices from 0 to and end indices from to . For each pair , we calculate the total number of ones after flipping the range using the formula above and update the maximum number of ones found so far. We can initialize our result with the number of zeros in the original array, , as a starting lower bound (as we want to at least achieve this number of ones by flipping entire array) and then try to maximize from there.
Time complexity:
Space complexity:
Approach 2: Optimizing the Brute Force to Linear Time
For a given flip range (), the number of ones in the modified array is given by:
Our goal is to maximize this expression by choosing the optimal range .
Let’s rewrite the expression for clarity:
We want to maximize over all possible . Since is constant, we need to maximize .
We can fix the ending index and then find the optimal starting index that maximizes . For a fixed , let’s analyze as a function of :
Let (which is constant for a fixed ). And let . Then . To maximize for a fixed and varying , we need to maximize for .
Let . Then the maximum value of for a fixed is .
We can precompute for all from to . Then, we can compute the prefix maximum of . Let . We can calculate iteratively: , and for , .
Finally, we can iterate through all possible ending indices from to . For each , calculate , the maximum number of ones after a flip ending at index is . We take the maximum of these values over all .
Time complexity:
Space complexity:
Approach 3: Using Kadane’s Algorithm
The core idea is to reframe the problem of maximizing ones after a flip into finding a maximum subarray sum and solve using kadane’s algorithm
Let’s consider the effect of flipping a bit.
- If we flip a 0, it becomes a 1, increasing the number of ones by 1 (i.e. +1)
- If we flip a 1, it becomes a 0, decreasing the number of ones by 1 (i.e. -1)
We can represent this change numerically. For each element in the original array, we transform it into a value such that represents the change in the number of ones when we flip .
We define the transformation as (you can verify it works, by putting 0 and 1):
So, we create a new transformed array where .
Now, if we flip a subarray in the original array , the net change in the total number of ones is the sum of the transformed values for in the range . Our goal is to find the subarray flip that maximizes the number of ones. This is same as finding the max subarray sum in . We can solve this using any linear time algo such as Kadane’s algorithm.
Let be the maximum subarray sum found. Let be the original number of ones in array . Then, the maximum number of ones we can achieve after one flip is .
Time complexity:
Space complexity:
Code
// Approach 1: O(n^2) Brute force + prefix sums
void solve() {
int n;
cin >> n;
vector<int> arr(n);
vector<int> cum(n + 1);
cum[0] = 0;
for (int i = 0; i < n; ++i) {
cin >> arr[i];
cum[i + 1] = cum[i] + arr[i];
}
int res = n - cum[n];
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
int ones = cum[j+1] - cum[i];
int zeros = j - i + 1 - ones;
int left_range_ones = cum[i];
int right_range_ones = cum[n] - cum[j+1];
res = max(res, left_range_ones + right_range_ones + zeros);
}
}
cout << res << endl;
}
// Approach 2: O(n) (Mathematical optimization of Brute force)
void solve() {
int n;
cin >> n;
vector<int> arr(n);
vector<int> cum(n + 1, 0);
for (int i = 0; i < n; ++i) {
cin >> arr[i];
cum[i + 1] = cum[i] + arr[i];
}
vector<int> k_values(n);
for (int i = 0; i < n; ++i) {
k_values[i] = -i + 2 * cum[i];
}
vector<int> m_values(n);
m_values[0] = k_values[0];
for (int i = 1; i < n; ++i) {
m_values[i] = max(m_values[i - 1], k_values[i]);
}
int max_ones = n - cum[n]; // Initial number of zeros
for (int j = 0; j < n; ++j) {
int c_j = (j + 1) - 2 * cum[j + 1];
int current_ones = cum[n] + m_values[j] + c_j;
max_ones = max(max_ones, current_ones);
}
cout << max_ones << endl;
}
// Approach 3: O(n) Kadane's algo
void solve() {
int n;
cin >> n;
vector<int> a(n);
int original_ones = 0;
for (int i = 0; i < n; ++i) {
cin >> a[i];
if (a[i] == 1) {
original_ones++;
}
}
vector<int> b(n);
for (int i = 0; i < n; ++i) {
b[i] = 1 - 2 * a[i];
}
int max_so_far = b[0];
int current_max = b[0];
for (int i = 1; i < n; ++i) {
current_max = max(b[i], current_max + b[i]);
max_so_far = max(max_so_far, current_max);
}
cout << original_ones + max_so_far << endl;
}