AMAZON Coding Question – Solved

7 Live
Your team at Amazon is working on a system that divides applications to a mixed cluster of computing devices. Each application is identified by an integer ID, requires a fixed non-zero amount of memory to execute, and is defined to be either a foreground or background application. IDs are guaranteed to be unique within their own application type, but not across types. Each device should be assigned two applications at once, one foreground application and one background application. Devices have limited amounts of memory and cannot execute applications that require more memory than the available memory. The goal of the system is to maximize the total utilization of the memory of a given device. A foreground/background application pair is considered to be optimal if there does not exist another pair that uses more memory than this pair, and also has a total less than or equal to the total memory of the device. For example, if the device has 10MB memory, a foreground/background pair using a sum total of 9MB memory would be optimal if there does not exist a pair that uses a sum total of 10MB, but would not be optimal if such a pair did exist. Write an algorithm to find the sets of foreground and background application pairs that optimally utilize the given device for a given list of foreground applications and a given list of background applications. Input The input to the function/method consists of three arguments: deviceCapacity, an integer representing the maximum capacity of the given device; foregroundAppList, a list of pairs of integers where the first integer represents the unique ID of a foreground application and the second integer represents the amount of memory required by this application; backgroundAppList, a list of pairs of integers where the first integer represents the unique ID of a background application and the second integer represents the amount of memory required by this application. Output Return a list of pairs of integers representing the pairs of IDs of foreground and background applications that optimally utilize the given device [foregroundAppID, backgroundAppID]. If no pair is possible, return a list with empty pair - not just an empty list. Examples Example 1: Input: deviceCapacity = 7 foregroundAppList = [[1,2], [2,4], [3,6]] backgroundAppList = [[1,2]] Output: [[2,1]] Explanation: There are only three combinations, [1,1], [2,1], and [3,1], which use a total of 4, 6, and 8MB memory, respectively. Since 6 is the largest use that does not exceed 7, [2,1] is the only optimal pair. Example 2: Input: deviceCapacity = 10 foregroundAppList = [[1, 3], [2, 5], [3, 7], [4, 10]] backgroundAppList = [[1, 2], [2, 3], [3, 4], [4, 5]] Output: [[2, 4], [3, 2]] Explanation: There are two pairs of foreground and background applications possible that optimally utilize the given device. Application 2 from foregroundAppList uses 5 memory units, and application 4 from backgroundAppList also uses 5 memory units. Combined, they add up to 10 units of memory. Similarly, application 3 from foregroundAppList uses...

Asked in: AMAZON

Image of the Question

Question Image Question Image Question Image

All Testcases Passed βœ”



Passcode Image

Solution


def applicationPairs(deviceCapacity, foregroundAppList, backgroundAppList):
    # Write your code here
    foregroundAppList.sort(key = lambda x: (x[1],x[0]))
    backgroundAppList.sort(key = lambda x: (x[1],x[0]))
// ... rest of solution available after purchase

πŸ”’ Please login to view the solution

Explanation


```
This problem focuses on pairing foreground and background applications to maximize the utilization of a device's memory capacity without exceeding it. The goal is to find all pairs whose combined memory usage is as large as possible but still less than or equal to the device capacity. Here is how you should think about the problem and approach it:

---

### Understanding the Problem

We have three inputs:
- A device capacity (an integer), representing the maximum memory the device can use.
- A list of foreground applications, each defined by a unique ID and the memory it requires.
- A list of background applications, similarly defined.

We must pair exactly one foreground and one background application such that their total memory usage is as close as possible to the device capacity without going over. If multiple pairs achieve the same maximum usage, all those pairs must be returned.

If no pair can fit within the capacity, return a list containing a single empty pair.

---

### Step 1: Identify the problem as a variation of the "Two Sum" or "Pair Sum" problem with constraints

The problem essentially asks:
From two lists of memory usages, find pairs whose sums are maximized but do not exceed a given target (device capacity).

The twist is that we also need to return all pairs that reach this maximum sum and the lists represent applications with IDs, so we must keep track of IDs.

---

### Step 2: Naive approach - brute force

One straightforward way is to:
- Iterate through each foreground application.
- For each foreground app, iterate through all background apps.
- Calculate the sum of their memory requirements.
- Track the sums that are less than or equal to the device capacity.
- Maintain the maximum sum encountered and store all pairs achieving this sum.

**Drawbacks:**
- This approach has a time complexity of O(F * B), where F and B are the lengths of foreground and background lists respectively.
- For large inputs, this approach can be computationally expensive.

---

### Step 3: Optimized approach using sorting and two pointers

To improve efficiency, you can use sorting and a two-pointer technique.

- First, sort the foreground apps by their memory usage.
- Sort the background apps by their memory usage as well.

Then:

- Initialize two pointers: one starting at the beginning of the foreground list (lowest memory usage), and the other starting at the end of the background list (highest memory usage).
- Calculate the sum of the pair at these pointers.
- If the sum is greater than the device capacity, move the pointer in the background list backward (to reduce sum).
- If the sum is less than or equal to the capacity:
- Check if the sum is greater than the current maximum sum found:
- If yes, update the maximum sum and reset the results list with this pair.
- If equal, add the pair to the results list.
- Move the pointer in the foreground list forward (to potentially increase sum).

Continue this process until one of the pointers goes out of bounds.

This technique leverages the sorted order to efficiently navigate possible sums and avoids checking every pair explicitly.

---

### Step 4: Handling multiple pairs with same memory usage

It is possible that multiple foreground or background applications have the same memory usage. When you find a pair with memory sum equal to the current maximum:

- You must check for all other applications with the same memory values on either side.
- For example, if multiple foreground apps have the same memory usage, and the background app pointer is fixed, add all these pairs.
- Similarly, if multiple background apps have the same memory usage, add those pairs as well.

This may require scanning neighbors with the same memory values on either list to find all combinations contributing to the same optimal sum.

---

### Step 5: Edge cases and no solution scenario

- If no pair's sum is less than or equal to the device capacity, return a list containing an empty pair.
- If device capacity is very small and no single foreground or background app fits, the same no solution condition applies.
- Consider inputs where either list is empty β€” then no valid pair exists.

---

### Step 6: Memory and time complexity considerations

- Sorting both lists takes O(F log F + B log B).
- The two-pointer traversal takes O(F + B).
- Collecting pairs with identical values may require additional linear scans but overall stays efficient.

---

### Step 7: Summary of the approach

1. Sort foreground and background applications by memory usage.
2. Use two pointers (start of foreground, end of background) to find sums closest to capacity without exceeding it.
3. Track maximum sum found and store pairs corresponding to this sum.
4. Handle duplicates in memory usage to add all valid pairs.
5. Return the list of optimal pairs, or an empty pair if none fit.

---

### Additional thoughts

- Using appropriate data structures to group or index applications by memory usage can help in efficiently finding duplicates.
- While traversing with two pointers, careful bookkeeping is required to avoid missing pairs with identical memory usage.
- This problem resembles a variation of the "knapsack" problem but simplified to exactly two items and a fixed capacity, allowing efficient pairing strategies instead of full DP.

---

By following this direction, you will develop an efficient algorithm that produces all optimal foreground/background application pairs that best utilize device memory.
```


Related Questions