Skip to content

Apartments

Apartments
CSES
↗

Abridged problem statement

Given two multisets AA and BB, and a number kk, find the size of a maximum matching given that two elements a∈A,b∈Ba \in A, b \in B can be matched iff ∣a−b∣≤k|a-b| \le k.

That is, form as many pairs (a,b)(a,b) with a∈A,b∈Ba \in A, b \in B as possible, using every element at most once.

You should take a minute to think about why the original problem statement is equivalent to this abridged version.

Solution

Again, with most greedy problems, there’s really no good way to begin. However, there are things we can try. Allow me to introduce you to another tool you should add to your toolkit: the extremal principle.

The idea behind this problem-solving technique is simple–a complicated problem may be simpler to think about if we consider ’extremal’ cases—that is, cases involving the minimum and maximum elements.

Let’s think about the minimum element across both AA and BB, call this mm, and let’s assume that m∈Am \in A. Maybe our two sets look something like this:

> k
The minimum elements of the two sets are more than k apart.

Here, the blue circles denote elements in AA (the leftmost blue element is mm), and the orange circles denote elements in BB.

In this case, we cannot match mm with any element in BB. Thus, it seems reasonable to assume that the answer will remain unchanged if we remove mm from AA?

A←A∖{m} A \leftarrow A \setminus \{m\}

But what if the distance between ma:=min⁡{A}m_a:=\min\{A\} and mb:=min⁡{B}m_b:=\min\{B\} was ≤k\le k?

≤ k
The minimum elements of the two sets are at most k apart.

We could then match mam_a with mbm_b, but should we?

An optimal matching?

Suppose we already have an optimal matching. If it is possible to modify this matching so that it:

  • Remains valid
  • Now includes (ma,mb)(m_a, m_b)
  • Has the same (or better) cardinality as before

then we will have successfully proven that there exists an optimal matching containing (ma,mb)(m_a, m_b). It remains to be seen whether or not this is possible.

To begin with, consider this extremely simple case:

ma and mb are not matched with anything.

We can simply match mam_a with mbm_b without making any other changes. Next, consider this case:

mb is matched with an element other than ma, which is not matched.

We can include (ma,mb)(m_a, m_b) by removing the existing matching mbm_b is a part of and matching it with mam_a instead. The number of matchings remains unchanged.

Replace the previous match containing mB with the pair (mA, mB).

The most interesting case is when both mam_a and mbm_b are matched:

The matching contains a criss-cross.

I claim that the following replacement is possible:

The crossed pairs can be replaced without changing the matching’s cardinality.

The proof is straightforward. Let the two matched pairs be (ma,B1)(m_a,B_1) and (A1,mb)(A_1,m_b).

We already know that (ma,mb)(m_a,m_b) is valid because ∣ma−mb∣≤k|m_a-m_b|\le k. We only need to prove that (A1,B1)(A_1,B_1) is also valid.

Since mam_a and mbm_b are the minimum elements of their respective sets,

ma≤A1andmb≤B1. m_a\le A_1 \qquad\text{and}\qquad m_b\le B_1.

Now consider the relative order of A1A_1 and B1B_1:

  • If B1<A1B_1<A_1 (this is what’s depicted in the above diagram), then
A1−B1≤A1−mb≤k, A_1-B_1\le A_1-m_b\le k,

because (A1,mb)(A_1,m_b) was a valid pair.

  • If A1≤B1A_1\le B_1, then
B1−A1≤B1−ma≤k, B_1-A_1\le B_1-m_a\le k,

because (ma,B1)(m_a,B_1) was a valid pair.

Therefore, ∣A1−B1∣≤k|A_1-B_1|\le k in either case. Thus, we can replace the pairs (ma,B1)(m_a,B_1) and (A1,mb)(A_1,m_b) with (ma,mb)(m_a,m_b) and (A1,B1)(A_1,B_1) without changing the matching’s cardinality.

If this is your first time solving this problem, keep in mind that this is a hard and novel idea, and you may benefit from a re-read. Anyway, with all that work, we’ve concluded that matching (ma,mb)(m_a, m_b) (when we can) is always optimal. Then, we set

A←A∖{ma} A \leftarrow A \setminus \{m_a\}

and

B←B∖{mb} B \leftarrow B \setminus \{m_b\}

When we can’t, it’s safe to discard the smaller of the two.

Implementation

A simple two-pointer approach suffices.

#include <bits/stdc++.h>
 
using namespace std;
 
int main() {
  int n, m, k;
  cin >> n >> m >> k;
 
  vector<int> a(n), b(m);
  for (int &i : a) {
    cin >> i;
  }
  for (int &i : b) {
    cin >> i;
  }
 
  sort(a.begin(), a.end());
  sort(b.begin(), b.end());
 
  int ans = 0;
 
  int i = 0, j = 0;
  while (i < int(a.size()) && j < int(b.size())) {
    if (abs(a[i] - b[j]) > k) {
      if (a[i] < b[j]) {
        i++;
      } else {
        j++;
      }
      continue;
    }
    ans++;
    i++, j++;
  }
 
  cout << ans << '\n';
}