ABC227 D - Project Planning C++解答例
ABC227 D - Project PlanningをC++で解きます。
問題
問題文をAtCoderのページより引用します。
問題文
キーエンスには
個の部署があり、 N 番目の部署には i(1 \leq i \leq N) 人の社員が所属しています。異なる部署に同じ社員が所属していることはありません。 A_i キーエンスは、部署をまたいだ全社横断プロジェクトを計画しています。1つのプロジェクトは
個の相異なる部署から1人ずつ選出して作り、ちょうど K 人から構成されるようにします。 K プロジェクトは最大でいくつ作れますか?ただし、1人が複数のプロジェクトに参加することはできません。
制約
1 \leq K \leq N \leq 2 \times 10^5 1 \leq A_i \leq 10^{12} - 入力はすべて整数
解答例
二分探索による最大値探索を行う
制約条件を見ると、1つの部署に所属している社員の人数
プロジェクトを作るには
(例えば
この問題で求められている答えは、作成可能なプロジェクトの最大個数です。愚直に解くことはできない、かつ実現可能な最大値(最小値)を求めるような問題は、二分探索によって解くことを思いつきます。
作成するプロジェクトの個数を決め打ちする
求めるのはプロジェクトの最大個数なので、これを決め打ちする二分探索をすることを考えます。
作成するプロジェクトの個数を
二分探索の基本骨子は以下のようなコードになります。
int64_t left = 0;
int64_t right = 適当な最大値;
while (right - left > 1) {
int64_t mid = (left + right) / 2;
bool ok = true;
~~何かしらの条件判定~~
if (ok) {
left = mid;
} else {
right = mid;
}
}
ここでの何かしらの条件判定とは、「
M 個のプロジェクトを作成できる条件
今、
図の
ここで、ある部署
もし
一方で、もし
これを数式で表してみます。部署
つまり、各部署の
逆に
二分探索の探索範囲
プロジェクトが作成可能かどうかを判定する条件ができたので、二分探索を実装します。
判定条件をプログラムで実装すると、次のようなコードになります。
int64_t left = 0;
int64_t right = 適当な最大値;
while (right - left > 1) {
int64_t mid = (left + right) / 2;
int64_t sum = 0;
for (int64_t i = 0; i < N; i++) {
sum += std::min(mid, A.at(i));
}
if (sum >= K * mid) {
left = mid;
} else {
right = mid;
}
}
二分探索の探索範囲(右側)ですが、
実装例
コード全体は以下のようになります。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main() {
int64_t N, K;
std::cin >> N >> K;
std::vector<int64_t> A(N);
for (int64_t i = 0; i < N; i++) {
std::cin >> A.at(i);
}
int64_t left = 0;
int64_t right = 9000000000000000000 / K;
while (right - left > 1) {
int64_t mid = (left + right) / 2;
int64_t sum = 0;
for (int64_t i = 0; i < N; i++) {
sum += std::min(mid, A.at(i));
}
if (sum >= K * mid) {
left = mid;
} else {
right = mid;
}
}
std::cout << left << std::endl;
return 0;
}
実際に提出したコードはこちら。
Discussion