Dynamic Programming · 26 of 60
Maximum sum of pairs with specific difference
Make partner pairs of close numbers so the paired numbers add up to the biggest total, with nobody in two pairs.
- Simple way
- time O(2^n)
room O(n) - Clever way
- time O(n log n)
room O(n)
The problem, simply
You get a list of numbers and a number K. Two numbers may form a pair if their difference is strictly less than K. Each number can be in at most one pair, and some numbers may stay alone.
Give back the biggest possible sum of all numbers that end up in pairs.
A story to remember it
Picture it
After sorting, close numbers sit side by side. Pairs must differ by less than 4. Pairing 3-5, 10-12 and 15-17 gives 62, and 9 stays alone.
- answer
- ignored
Examples
A normal case
- In
arr = [3, 5, 10, 15, 17, 12, 9], K = 4- Out
62
Pairs 3 and 5, 10 and 12, 15 and 17. Their sum is 8 + 22 + 32 = 62. 9 could pair with 10, but then 12 is left alone and the total is lower.
Only one pair works
- In
arr = [5, 15, 10, 300], K = 12- Out
25
10 and 15 differ by 5. Every other pair is 12 or more apart.
Nobody can pair
- In
arr = [1, 10, 20], K = 5- Out
0
All gaps are 5 or more, so no pairs and a sum of 0.
Equal numbers
- In
arr = [4, 4, 4], K = 1- Out
8
Two 4s differ by 0, which is less than 1. The third 4 has no partner left.
Way 1: the simple way
Sort the list. Then from the biggest number down, each number either stays alone or pairs with its left neighbor.
f(i)is the best sum using sorted numbers 0 toi.- Skip number
i:f(i - 1). - If
a[i] - a[i - 1] < K, pair them:a[i] + a[i - 1] + f(i - 2). f(0)and below are 0.
Why only the left neighbor? After sorting, the left neighbor is the closest smaller number. Any pair that reaches further back can swap partners with the neighbor and stay valid with the same or a bigger sum.
Sorted first five: [3, 5, 9, 10, 12]. f(i) is the best using numbers 0 to i. Each call either skips number i or pairs it with number i - 1. f(2) is solved twice and f(1) three times. 9 and 5 differ by 4, so f(2) has no pair branch.
- seen
import java.util.Arrays;
class Solution {
public int maxSumPairs(int[] arr, int k) {
int[] a = arr.clone();
Arrays.sort(a); // partners stand side by side
return best(a, a.length - 1, k);
}
private int best(int[] a, int i, int k) {
if (i <= 0) return 0; // zero or one number left
int skip = best(a, i - 1, k);
int pair = 0;
if (a[i] - a[i - 1] < k) {
pair = a[i] + a[i - 1] + best(a, i - 2, k); // pair with the left neighbor
}
return Math.max(skip, pair);
}
}How long it takes: up to two branches per call, O(2^n) (it grows like the Fibonacci numbers). Extra room: the waiting calls, O(n).
Way 2: the clever way
Remember each f(i) in an array dp and fill it from left to right.
- Sort. Set
dp[0] = 0. - For
ifrom 1:dp[i] = dp[i - 1](skip). - If
a[i] - a[i - 1] < K, also trya[i] + a[i - 1] + dp[i - 2](withdp[-1] = 0). - The answer is
dp[n - 1].
Pair with the left neighbor or skip
01 / 07
Sort first: 3, 5, 9, 10, 12, 15, 17. With only the number 3 there is nobody to pair with, so the best is 0.
- looking now
- checking
- answer
- rejected
- just added
import java.util.Arrays;
class Solution {
public int maxSumPairs(int[] arr, int k) {
int n = arr.length;
if (n < 2) return 0;
int[] a = arr.clone();
Arrays.sort(a);
int[] dp = new int[n]; // dp[0] = 0: one number cannot pair
for (int i = 1; i < n; i++) {
dp[i] = dp[i - 1]; // leave a[i] alone
if (a[i] - a[i - 1] < k) {
int before = i >= 2 ? dp[i - 2] : 0;
dp[i] = Math.max(dp[i], before + a[i] + a[i - 1]); // pair the neighbors
}
}
return dp[n - 1];
}
}How long it takes: sorting is O(n log n), the fill is O(n). Extra room: the dp array and the sorted copy, O(n).
Way 3: greedy from the top
Since we want a big sum, the biggest numbers matter most. Walk the sorted list from the right. If the biggest remaining number can pair with its left neighbor, pair them and jump two steps. Otherwise drop it and step once.
This is safe because the top number, if it can pair at all, gains the most from pairing now, and its neighbor is its best partner.
import java.util.Arrays;
class Solution {
public int maxSumPairs(int[] arr, int k) {
int[] a = arr.clone();
Arrays.sort(a);
int sum = 0;
int i = a.length - 1;
while (i > 0) {
if (a[i] - a[i - 1] < k) { // the two biggest left can pair
sum += a[i] + a[i - 1];
i -= 2;
} else {
i -= 1; // a[i] has no partner
}
}
return sum;
}
}Same O(n log n) time, but only O(1) extra room besides the sort. It works because all numbers are added, never subtracted. With negative numbers you would want the DP, because pairing two negatives lowers the sum.
Watch out for
- Forgetting to sort. Without sorting, good partners are not neighbors and the DP misses them.
- Sorting the caller's array in place. Clone first if the input must not change.
dp[i - 2]wheni = 1goes out of bounds. Treat it as 0.
What interviewers ask next
- Count the most pairs instead of the biggest sum? Same sort, then greedy from the left: pair neighbors whenever they fit.
- What if numbers can be negative? Use the DP and allow "skip" even when a pair fits. The greedy can fail.
- Why are we allowed to sort? The answer only depends on which numbers end up together, not on where they stood, so any order is fine.
- Return the pairs. Walk
dpbackwards and see whether eachdp[i]came from skipping or pairing.
Remember this
Grown-up words: lining up kids is sorting, partners are disjoint pairs, the note at each spot is dp[i], swapping partners is an exchange argument, and Way 3 is a greedy that only works because every value is positive.