Before attempting this problem, you should be comfortable with:
Hash Maps - Using dictionaries to group and track maximum values by key
Sorting - Sorting values to find the top k elements efficiently
Heaps/Priority Queues - Maintaining the k largest elements using a min-heap of fixed size
1. Hash Map
Intuition
We need to pick three indices with distinct x-values and maximize the sum of their corresponding y-values. For each unique x-value, we only care about the maximum y-value associated with it, since choosing a smaller y-value for the same x would never be optimal.
After collecting the best y-value for each distinct x, we simply need the three largest values. If there are fewer than three distinct x-values, the answer is -1.
Algorithm
Build a hash map where each key is an x-value and the value is the maximum y-value seen for that x.
If the map has fewer than 3 entries, return -1.
Extract all the y-values from the map and sort them.
publicclassSolution{publicintmaxSumDistinctTriplet(int[] x,int[] y){Map<Integer,Integer> mp =newHashMap<>();for(int i =0; i < x.length; i++){int key = x[i], val = y[i];
mp.put(key,Math.max(mp.getOrDefault(key,0), val));}if(mp.size()<3)return-1;List<Integer> vals =newArrayList<>(mp.values());Collections.sort(vals);int n = vals.size();return vals.get(n-1)+ vals.get(n-2)+ vals.get(n-3);}}
classSolution{public:intmaxSumDistinctTriplet(vector<int>& x, vector<int>& y){
unordered_map<int,int> mp;for(int i =0; i < x.size(); i++){int key = x[i], val = y[i];
mp[key]=max(mp[key], val);}if(mp.size()<3)return-1;
vector<int> vals;
vals.reserve(mp.size());for(auto&p : mp) vals.push_back(p.second);sort(vals.begin(), vals.end());int n = vals.size();return vals[n-1]+ vals[n-2]+ vals[n-3];}};
classSolution{/**
* @param {number[]} x
* @param {number[]} y
* @return {number}
*/maxSumDistinctTriplet(x, y){const mp =newMap();for(let i =0; i < x.length; i++){const key = x[i], val = y[i];
mp.set(key, Math.max(mp.get(key)||0, val));}if(mp.size <3)return-1;const vals = Array.from(mp.values()).sort((a, b)=> a - b);const n = vals.length;return vals[n-1]+ vals[n-2]+ vals[n-3];}}
publicclassSolution{publicintMaxSumDistinctTriplet(int[] x,int[] y){var mp =newDictionary<int,int>();for(int i =0; i < x.Length; i++){int key = x[i], val = y[i];if(mp.TryGetValue(key,outvar existing)){
mp[key]= Math.Max(existing, val);}else{
mp[key]= val;}}if(mp.Count <3)return-1;var vals = mp.Values.ToList();
vals.Sort();int n = vals.Count;return vals[n-1]+ vals[n-2]+ vals[n-3];}}
funcmaxSumDistinctTriplet(x []int, y []int)int{
mp :=make(map[int]int)for i :=0; i <len(x); i++{
key, val := x[i], y[i]if existing, ok := mp[key]; ok {if val > existing {
mp[key]= val
}}else{
mp[key]= val
}}iflen(mp)<3{return-1}
vals :=make([]int,0,len(mp))for_, v :=range mp {
vals =append(vals, v)}
sort.Ints(vals)
n :=len(vals)return vals[n-1]+ vals[n-2]+ vals[n-3]}
class Solution {funmaxSumDistinctTriplet(x: IntArray, y: IntArray): Int {val mp = mutableMapOf<Int, Int>()for(i in x.indices){val key = x[i]val value = y[i]
mp[key]=maxOf(mp.getOrDefault(key,0), value)}if(mp.size <3)return-1val vals = mp.values.sortedDescending()return vals[0]+ vals[1]+ vals[2]}}
classSolution{funcmaxSumDistinctTriplet(_ x:[Int],_ y:[Int])->Int{var mp =[Int:Int]()for i in0..<x.count {let key = x[i], val = y[i]
mp[key]=max(mp[key]??0, val)}if mp.count <3{return-1}let vals = mp.values.sorted()let n = vals.count
return vals[n-1]+ vals[n-2]+ vals[n-3]}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(n)
2. Hash Map + Min-Heap
Intuition
Instead of sorting all values to find the top 3, we can use a min-heap of size 3. As we iterate through the distinct y-values, we maintain only the three largest seen so far. This avoids the overhead of sorting the entire collection.
Whenever the heap exceeds size 3, we remove the smallest element. After processing all values, the heap contains exactly the three largest y-values (if at least 3 exist).
Algorithm
Build a hash map mapping each x-value to its maximum y-value.
classSolution:defmaxSumDistinctTriplet(self, x: List[int], y: List[int])->int:
mp ={}for xi, yi inzip(x, y):
mp[xi]=max(mp.get(xi,0), yi)
minHeap =[]for val in mp.values():
heapq.heappush(minHeap, val)iflen(minHeap)>3:
heapq.heappop(minHeap)return-1iflen(minHeap)<3elsesum(minHeap)
publicclassSolution{publicintmaxSumDistinctTriplet(int[] x,int[] y){Map<Integer,Integer> mp =newHashMap<>();for(int i =0; i < x.length; i++){
mp.put(x[i],Math.max(mp.getOrDefault(x[i],0), y[i]));}PriorityQueue<Integer> pq =newPriorityQueue<>();for(int val : mp.values()){
pq.offer(val);if(pq.size()>3){
pq.poll();}}if(pq.size()<3){return-1;}int sum =0;while(!pq.isEmpty()){
sum += pq.poll();}return sum;}}
classSolution{public:intmaxSumDistinctTriplet(vector<int>& x, vector<int>& y){
unordered_map<int,int> mp;for(int i =0; i < x.size(); i++){
mp[x[i]]=max(mp[x[i]], y[i]);}
priority_queue<int, vector<int>, greater<int>> pq;for(auto&p : mp){
pq.push(p.second);if(pq.size()>3){
pq.pop();}}if(pq.size()<3)return-1;int sum =0;while(!pq.empty()){
sum += pq.top();
pq.pop();}return sum;}};
classSolution{/**
* @param {number[]} x
* @param {number[]} y
* @return {number}
*/maxSumDistinctTriplet(x, y){const mp =newMap();for(let i =0; i < x.length; i++){
mp.set(x[i], Math.max(mp.get(x[i])||0, y[i]));}const heap =newMinPriorityQueue();for(const val of mp.values()){
heap.enqueue(val);if(heap.size()>3){
heap.dequeue();}}if(heap.size()<3){return-1;}return heap.dequeue()+ heap.dequeue()+ heap.dequeue();}}
publicclassSolution{publicintMaxSumDistinctTriplet(int[] x,int[] y){var mp =newDictionary<int,int>();for(int i =0; i < x.Length; i++){int key = x[i], val = y[i];if(mp.TryGetValue(key,outvar prev)){
mp[key]= Math.Max(prev, val);}else{
mp[key]= val;}}var pq =newPriorityQueue<int,int>();foreach(var val in mp.Values){
pq.Enqueue(val, val);if(pq.Count >3){
pq.Dequeue();}}if(pq.Count <3){return-1;}int sum =0;while(pq.Count >0){
sum += pq.Dequeue();}return sum;}}
funcmaxSumDistinctTriplet(x []int, y []int)int{
mp :=make(map[int]int)for i :=0; i <len(x); i++{
key, val := x[i], y[i]if existing, ok := mp[key]; ok {if val > existing {
mp[key]= val
}}else{
mp[key]= val
}}
minHeap :=&IntHeap{}
heap.Init(minHeap)for_, val :=range mp {
heap.Push(minHeap, val)if minHeap.Len()>3{
heap.Pop(minHeap)}}if minHeap.Len()<3{return-1}
sum :=0for minHeap.Len()>0{
sum += heap.Pop(minHeap).(int)}return sum
}type IntHeap []intfunc(h IntHeap)Len()int{returnlen(h)}func(h IntHeap)Less(i, j int)bool{return h[i]< h[j]}func(h IntHeap)Swap(i, j int){ h[i], h[j]= h[j], h[i]}func(h *IntHeap)Push(x any){*h =append(*h, x.(int))}func(h *IntHeap)Pop() any {
old :=*h
n :=len(old)
x := old[n-1]*h = old[0: n-1]return x
}
class Solution {funmaxSumDistinctTriplet(x: IntArray, y: IntArray): Int {val mp = mutableMapOf<Int, Int>()for(i in x.indices){val key = x[i]val value = y[i]
mp[key]=maxOf(mp.getOrDefault(key,0), value)}val pq = PriorityQueue<Int>()for(value in mp.values){
pq.offer(value)if(pq.size >3){
pq.poll()}}if(pq.size <3)return-1var sum =0while(pq.isNotEmpty()){
sum += pq.poll()}return sum
}}
classSolution{funcmaxSumDistinctTriplet(_ x:[Int],_ y:[Int])->Int{var mp =[Int:Int]()for i in0..<x.count {let key = x[i], val = y[i]
mp[key]=max(mp[key]??0, val)}var vals =Array(mp.values)
vals.sort()if vals.count <3{return-1}let n = vals.count
return vals[n-1]+ vals[n-2]+ vals[n-3]}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
3. Greedy
Intuition
We can track the top 3 candidates in constant space by maintaining a sorted list of the best 3 (x, y) pairs seen so far. For each new element, we either update an existing entry (if the x-value matches) or insert it if the y-value is large enough to make the top 3.
The key insight is that we only ever need to compare against at most 3 entries. When an x-value already exists in our top 3, we update its y-value if the new one is larger and re-sort. Otherwise, we check if the new y-value can replace the smallest of our current top 3.
Algorithm
Maintain an array best of 3 pairs (x, y), initialized with sentinel values (like negative infinity for y).
For each (xi, yi) in the input:
If xi matches any x in best, update that entry's y-value if yi is larger, then re-sort.
Otherwise, insert (xi, yi) into the appropriate position if yi is larger than any of the current top 3 values.
If the smallest y-value in best is still a sentinel, return -1.
classSolution:defmaxSumDistinctTriplet(self, x: List[int], y: List[int])->int:
best =[(None,float("-inf"))]*3for xi, yi inzip(x, y):for i,(xj, yj)inenumerate(best):if xi == xj:if yi > yj:
best[i]=(xi, yi)
best.sort(key=lambda t: t[1], reverse=True)breakelse:if yi > best[0][1]:
best =[(xi, yi), best[0], best[1]]elif yi > best[1][1]:
best =[best[0],(xi, yi), best[1]]elif yi > best[2][1]:
best[2]=(xi, yi)returnsum(v for _, v in best)if best[2][1]>float("-inf")else-1
publicclassSolution{publicintmaxSumDistinctTriplet(int[] x,int[] y){List<int[]> best =newArrayList<>();for(int i =0; i <3; i++){
best.add(newint[]{Integer.MIN_VALUE,Integer.MIN_VALUE});}for(int idx =0; idx < x.length; idx++){int xi = x[idx], yi = y[idx];boolean updated =false;for(int i =0; i <3; i++){if(best.get(i)[0]== xi){if(yi > best.get(i)[1]){
best.get(i)[1]= yi;
best.sort((a, b)->Integer.compare(b[1], a[1]));}
updated =true;break;}}if(updated)continue;if(yi > best.get(0)[1]){
best.add(0,newint[]{xi, yi});
best.remove(3);}elseif(yi > best.get(1)[1]){
best.add(1,newint[]{xi, yi});
best.remove(3);}elseif(yi > best.get(2)[1]){
best.get(2)[1]= yi;
best.get(2)[0]= xi;}}if(best.get(2)[1]==Integer.MIN_VALUE){return-1;}return best.get(0)[1]+ best.get(1)[1]+ best.get(2)[1];}}
classSolution{/**
* @param {number[]} x
* @param {number[]} y
* @return {number}
*/maxSumDistinctTriplet(x, y){let best =[[null,-Infinity],[null,-Infinity],[null,-Infinity]];for(let i =0; i < x.length; i++){const xi = x[i], yi = y[i];let updated =false;for(let j =0; j <3; j++){if(best[j][0]=== xi){if(yi > best[j][1]){
best[j][1]= yi;
best.sort((a, b)=> b[1]- a[1]);}
updated =true;break;}}if(updated)continue;if(yi > best[0][1]){
best =[[xi, yi], best[0], best[1]];}elseif(yi > best[1][1]){
best =[best[0],[xi, yi], best[1]];}elseif(yi > best[2][1]){
best[2]=[xi, yi];}}return best[2][1]===-Infinity?-1: best[0][1]+ best[1][1]+ best[2][1];}}
publicclassSolution{publicintMaxSumDistinctTriplet(int[] x,int[] y){var best =newList<(int xi,int yi)>{(int.MinValue,int.MinValue),(int.MinValue,int.MinValue),(int.MinValue,int.MinValue)};for(int i =0; i < x.Length; i++){int xi = x[i], yi = y[i];bool updated =false;for(int j =0; j <3; j++){if(best[j].xi == xi){if(yi > best[j].yi){
best[j]=(xi, yi);
best = best.OrderByDescending(t => t.yi).ToList();}
updated =true;break;}}if(updated)continue;if(yi > best[0].yi){
best =newList<(int,int)>{(xi, yi), best[0], best[1]};}elseif(yi > best[1].yi){
best =newList<(int,int)>{ best[0],(xi, yi), best[1]};}elseif(yi > best[2].yi){
best[2]=(xi, yi);}}return best[2].yi ==int.MinValue
?-1: best[0].yi + best[1].yi + best[2].yi;}}
funcmaxSumDistinctTriplet(x []int, y []int)int{const minInt =-1<<31
best :=[][2]int{{minInt, minInt},{minInt, minInt},{minInt, minInt}}for i :=0; i <len(x); i++{
xi, yi := x[i], y[i]
updated :=falsefor j :=0; j <3; j++{if best[j][0]== xi {if yi > best[j][1]{
best[j][1]= yi
sort.Slice(best,func(a, b int)bool{return best[a][1]> best[b][1]})}
updated =truebreak}}if updated {continue}if yi > best[0][1]{
best =[][2]int{{xi, yi}, best[0], best[1]}}elseif yi > best[1][1]{
best =[][2]int{best[0],{xi, yi}, best[1]}}elseif yi > best[2][1]{
best[2]=[2]int{xi, yi}}}if best[2][1]== minInt {return-1}return best[0][1]+ best[1][1]+ best[2][1]}
class Solution {funmaxSumDistinctTriplet(x: IntArray, y: IntArray): Int {var best =mutableListOf(intArrayOf(Int.MIN_VALUE, Int.MIN_VALUE),intArrayOf(Int.MIN_VALUE, Int.MIN_VALUE),intArrayOf(Int.MIN_VALUE, Int.MIN_VALUE))for(i in x.indices){val xi = x[i]val yi = y[i]var updated =falsefor(j in0 until 3){if(best[j][0]== xi){if(yi > best[j][1]){
best[j][1]= yi
best.sortByDescending{ it[1]}}
updated =truebreak}}if(updated)continueif(yi > best[0][1]){
best =mutableListOf(intArrayOf(xi, yi), best[0], best[1])}elseif(yi > best[1][1]){
best =mutableListOf(best[0],intArrayOf(xi, yi), best[1])}elseif(yi > best[2][1]){
best[2]=intArrayOf(xi, yi)}}returnif(best[2][1]== Int.MIN_VALUE)-1else best[0][1]+ best[1][1]+ best[2][1]}}
classSolution{funcmaxSumDistinctTriplet(_ x:[Int],_ y:[Int])->Int{var best:[[Int]]=[[Int.min,Int.min],[Int.min,Int.min],[Int.min,Int.min]]for i in0..<x.count {let xi = x[i], yi = y[i]var updated =falsefor j in0..<3{if best[j][0]== xi {if yi > best[j][1]{
best[j][1]= yi
best.sort {$0[1]>$1[1]}}
updated =truebreak}}if updated {continue}if yi > best[0][1]{
best =[[xi, yi], best[0], best[1]]}elseif yi > best[1][1]{
best =[best[0],[xi, yi], best[1]]}elseif yi > best[2][1]{
best[2]=[xi, yi]}}return best[2][1]==Int.min ?-1: best[0][1]+ best[1][1]+ best[2][1]}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Common Pitfalls
Not Taking Maximum Y for Each X
When multiple indices share the same x-value, only the maximum corresponding y-value matters. Using any y-value instead of tracking the maximum for each distinct x leads to suboptimal triplet selections.
Forgetting to Handle Fewer Than Three Distinct X-Values
If there are fewer than 3 distinct x-values in the input, no valid triplet exists. Failing to check this condition before computing the sum causes index-out-of-bounds errors or returns an invalid result instead of -1.