すごく典型的ないい問題! この手の問題は、大抵、二分探索法でもしゃくとり法でも解ける。
問題概要
2 つの数列 と が与えられる。
これらの数列から 1 つずつの値を選ぶ。選んだ値の差が 以下となるようにすることが可能かどうかを判定し、可能ならば選んだ値の和の最大値を求めよ。
制約
考えたこと
この問題は愚直に全探索で解こうと思うと、 から 1 個ずつ選ぶ方法をすべて試す の解法となる。これはもちろん間に合わない。
そんな時に有効な考え方は「ある量を固定して考える」というものだ。今回は から選ぶ要素を固定して考えよう。つまり、
for (int a : A) { }
の中身を埋めようというわけだ。
A の要素 a を固定した問題
から値 を選ぶとする。このとき、次のような問題になる。
のうち、 との値の差が 以下であるものがあるかを判定し、あるならばそのうちの最大値を求めよ
この処理を実現するためには、C++ ならば upper_bound()
を使うのが効果的だ。upper_bound
の使い方を次のコードに示す。このコードは、B[i] > v
を満たす最小の を求めるものになっている。計算量は である。
int i = upper_bound(B.begin(), B.end(), v) - B.begin();
なお、upper_bound()
を使うためには、 がソートされている必要があることに注意しよう (あらかじめ最初に をソートしておけばよい)。
ちなみに、upper_bound()
の原理については、次の記事に記した。
upper_bound()
を使うと、今回の問題は次のように解ける。
upper_bound()
を用いて、B[i] > a + D
を満たす最小の を求める- このとき、 は、
B[j] <= a + D
を満たす最大の となる- もし、
B[j] >= a - D
ならば、a + B[j]
が答えである - そうでなければ、 との差が 以下である要素は存在しないと言える
- もし、
この処理を、各 に対して実行すればよい。
全体の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; int main() { long long N, M, D; cin >> N >> M >> D; vector<long long> A(N), B(M); for (int i = 0; i < N; ++i) cin >> A[i]; for (int i = 0; i < M; ++i) cin >> B[i]; // B をソートしておく sort(B.begin(), B.end()); // a を固定する long long res = -1; for (long long a : A) { int i = upper_bound(B.begin(), B.end(), a + D) - B.begin(); --i; if (i < 0) continue; // これは B[0] > a + D であることを表す if (B[i] >= a - D) res = max(res, a + B[i]); } cout << res << endl; }
別解:しゃくとり法
も もソートしておく。
を小さい順に見ていきながら、 の方も小さい順に見ていく。
範囲 を照らすサーチライトを 上で動かしながら、それに照らされる物体を 上で動かしていくようなイメージだ。
具体的には、 の添字 と の添字 を管理する。最初は としておいて、
- を 1 増やす
B[j] <= a + D
を満たすギリギリのところまで を増やす
という操作を繰り返していく。
計算量は、しゃくとり法自体の部分は だが、 をそれぞれあらかじめソートする部分がボトルネックであり、 となる。
コード
#include <bits/stdc++.h> using namespace std; int main() { long long N, M, D; cin >> N >> M >> D; vector<long long> A(N), B(M); for (int i = 0; i < N; ++i) cin >> A[i]; for (int i = 0; i < M; ++i) cin >> B[i]; // A, B をソートしておく sort(A.begin(), A.end()); sort(B.begin(), B.end()); // しゃくとり法 long long res = -1; int j = 0; for (int i = 0; i < N; ++i) { // B[j] <= A[i] + D となる最大の j を求める while (j < M && B[j] <= A[i] + D) ++j; if (j > 0) --j; if (B[j] >= A[i] - D && B[j] <= A[i] + D) { res = max(res, A[i] + B[j]); } } cout << res << endl; }