Greedy · 35 of 35
Find maximum sum possible equal sum of three stacks
Lift blocks off the tops of three towers until all three towers weigh the same, keeping them as tall as possible.
- Simple way
- time O(n1 * n2 * n3)
room O(n1 + n2 + n3) - Clever way
- time O(n1 + n2 + n3)
room O(1)
The problem, simply
You get three stacks of positive numbers, given as arrays where index 0 is the top. You may only remove numbers from the top of a stack, as many as you like.
Remove numbers so that all three stacks have the same sum, and that sum is as big as possible. Give back that sum. If the only way is to empty them, the answer is 0.
A story to remember it
Picture it
The three stacks, drawn bottom to top. The arrays were [3, 2, 1, 1, 1], [4, 3, 2] and [1, 1, 4, 1], with index 0 on top.
Examples
A normal case
- In
s1 = [3, 2, 1, 1, 1], s2 = [4, 3, 2], s3 = [1, 1, 4, 1]- Out
5
Remove 3 from stack 1, 4 from stack 2, and 1 and 1 from stack 3. What is left adds up to 5, 5 and 5.
Already equal
- In
s1 = [2, 3], s2 = [5], s3 = [1, 4]- Out
5
Nothing needs to be removed.
No match except empty
- In
s1 = [1], s2 = [2], s3 = [3]- Out
0
The possible sums are 0 or 1, 0 or 2, and 0 or 3. Only 0 is shared.
Match deep down
- In
s1 = [9, 1, 1], s2 = [1, 1, 1, 1, 2], s3 = [7, 2]- Out
2
Stack 1 drops to 2, stack 2 drops to 2, stack 3 drops to 2.
Way 1: the simple way
Write down every sum each stack can still have, then look for the biggest sum that all three share.
- For each stack, compute the sum of everything from index
idown to the bottom, for everyi. These suffix sums are all the sums that stack can reach. - Try every triple of choices and keep the biggest sum where all three are equal.
class Solution {
public long maxEqualSum(int[] s1, int[] s2, int[] s3) {
long[] a = suffix(s1), b = suffix(s2), c = suffix(s3);
long best = 0;
for (long x : a)
for (long y : b)
for (long z : c)
if (x == y && y == z) best = Math.max(best, x);
return best;
}
// suf[i] = sum of s[i..end], the sum left after removing the top i numbers
private long[] suffix(int[] s) {
long[] suf = new long[s.length + 1];
for (int i = s.length - 1; i >= 0; i--) suf[i] = suf[i + 1] + s[i];
return suf;
}
}How long it takes: three nested loops, O(n1 * n2 * n3). Extra room: the three suffix arrays, O(n1 + n2 + n3).
Way 2: the clever way
Do what the kids did.
- Add up each stack.
- While the three sums are not equal, find the stack with the biggest sum and remove its top number. Move that stack's pointer down by one.
- If any stack runs out, the answer is 0. Otherwise the shared sum is the answer.
Why lift from the biggest? The final shared sum can never be more than the smallest current sum. The biggest stack must come down anyway, and lifting its top block is the smallest step it can take. It never jumps past a sum that the others could still reach.
Lift from the heaviest stack until all match
01 / 06
Add up each stack: 8, 9 and 7. They are not equal yet.
- sums
- 8 / 9 / 7
- answer
- rejected
class Solution {
public long maxEqualSum(int[] s1, int[] s2, int[] s3) {
long a = 0, b = 0, c = 0;
for (int x : s1) a += x;
for (int x : s2) b += x;
for (int x : s3) c += x;
int i = 0, j = 0, k = 0; // how many tops each stack has lost
while (!(a == b && b == c)) {
if (a >= b && a >= c) {
if (i == s1.length) return 0;
a -= s1[i++]; // lift from the heaviest stack
} else if (b >= a && b >= c) {
if (j == s2.length) return 0;
b -= s2[j++];
} else {
if (k == s3.length) return 0;
c -= s3[k++];
}
}
return a;
}
}How long it takes: every number is lifted at most once, O(n1 + n2 + n3). Extra room: a few counters, O(1).
Watch out for
- Sums can pass the
intrange when there are many big numbers. Uselong. - The loop must stop when all three are equal, including when all are 0.
- Ties for the biggest sum: lifting from any of the tied stacks is fine.
What interviewers ask next
- Why does lifting from the biggest never skip the answer? The answer is at most the smallest sum right now. The biggest stack is above that, so it must lose its top block in any solution. Numbers are positive, so lifting it can only bring it closer.
- What if numbers can be negative? Then sums can go up when you lift, and the greedy breaks. Put each stack's suffix sums in a hash set and look for the largest value in all three sets.
- What about
kstacks? Same idea with a max heap of sums, or note that the answer is the largest value that appears in every stack's suffix sum set. - What if you may remove from the bottom instead? Use prefix sums from the other end; the greedy is the same.
Remember this
Grown-up words: the towers are stacks, lifting the top block is a pop, the three counters are three pointers, and the sums a stack can still reach are its suffix sums.