C - 教室のエアコン配備 / Air Conditioning Installation in Classrooms Editorial by admin
Qwen3-Coder-480B概要
\(N\) 個の教室の老朽度のうち、最大 \(M\) 個を \(0\) に変更できるとき、変更後の老朽度の最大値を最小にする問題。
考察
この問題では、新しいエアコンを最大 \(M\) 台設置でき、それによって教室の老朽度を \(0\) にできます。目標は「全ての教室の老朽度の最大値」をできるだけ小さくすることです。
素朴な方法として、「老朽度の大きい方から \(M\) 個を \(0\) にする」と考えたくなります。しかし、それだと最適でないことがあります。例えば、老朽度が [100, 90, 10] で \(M=1\) の場合、最大値を \(90\) にできるのに、単純に最大を \(0\) にすると最大値が \(100\) になってしまいます。
そこで重要な観察があります: - 「最終的な老朽度の最大値を \(x\) 以下にできるか?」という判定問題を考えると、これは効率的に解けます。 - 老朽度が \(x\) より大きい教室には、必ず新しいエアコンを設置する必要があります。 - そのような教室の数が \(M\) 以下であれば、達成可能です。
このような「最大値の最小化」の問題では、二分探索が非常に有効です。探索範囲は \(0\) から \(\max(D_i)\) までで、ある値 \(x\) に対して条件を満たすかどうかを判定すればよいのです。
アルゴリズム
- 老朽度のリスト \(D\) を降順にソートします(大きい順)。
- 二分探索で「最終的な老朽度の最大値」の最小値を求めます。
- 各ステップで、ある値 \(x\) を「達成可能かどうか」判定します。
- \(D_i > x\) となる教室の数をカウントし、それが \(M\) 以下であれば達成可能。
- 二分探索の結果、達成可能な最小の最大値を出力します。
例
入力:
5 2
8 2 10 1 5
処理の流れ:
- ソート後: [10, 8, 5, 2, 1]
- 二分探索で最大値の候補を試す。
- 例えば最大値を 5 にしたいなら、老朽度が 10, 8 の2教室にエアコンを置けばOK → 必要台数は2 → \(M=2\) なのでOK。
計算量
- 時間計算量: \(O(N \log N + N \log(\max(D)))\)
- ソートに \(O(N \log N)\)、二分探索の各ステップで最大 \(N\) 個の要素を走査し、\(\log(\max(D))\) 回のステップが必要。
- 空間計算量: \(O(1)\)(入力データを除く)
実装のポイント
- 二分探索を用いる際は、
left,right,midの更新を慎重に行う。 - 判定関数
is_achievable()内では、ソート済みの配列を利用して、必要な台数を高速に求めること。 - 入力を高速に読み込むために
sys.stdin.readを使用している。
## ソースコード
```python
def main():
import sys
input = sys.stdin.read
data = input().split()
N = int(data[0])
M = int(data[1])
D = list(map(int, data[2:]))
# 老朽度を降順にソートする
D.sort(reverse=True)
# 最大値を最小化する → 二分探索
def is_achievable(max_val):
# 老朽度がmax_valを超える教室には新しいエアコンが必要
needed = 0
for d in D:
if d > max_val:
needed += 1
else:
break
return needed <= M
# 二分探索の範囲
left = 0
right = max(D)
answer = right
while left <= right:
mid = (left + right) // 2
if is_achievable(mid):
answer = mid
right = mid - 1
else:
left = mid + 1
print(answer)
if __name__ == "__main__":
main()
この解説は qwen3-coder-480b によって生成されました。
posted:
last update: