【ABC330】競プロのA-C問題を40代サラリーマンがPython解説!初心者にもわかりやすく

abc330 AtCoder

こんにちは、こんぶちゃ(茶コーダー)です!

初心者視点から競プロAtCoderのABC330のA問題〜C問題の自身の解答と解答説明をしていきます。

こんな人にオススメしたい
  • 競技プログラミング初心者で、問題の解き方がわからない方
  • AtCoderの公式解説は読んだが、内容が理解できなかった方
  • Pythonでの解法を知りたい方

まだまだ学習の身ですので、最適コードではない部分も多々あるかと思いますが、その部分は同じ学習者として温かい気持ちで応援&ご助言頂けば嬉しいです。

では早速行ってみましょう!

今回の参加コンテスト(ABC330)

AtCoder Beginner Contest 330

A問題:Counting Passes

A – Counting Passes

問題文

N 人の人 1,2,…,N がある試験を受け、人 i は Ai 点を取りました。
この試験では、 L 点以上を取った人のみが合格となります。N 人のうち何人が合格したか求めてください。

解答

# 入力から n(人数)と l(合格基準点)を受け取る
n, l = map(int, input().split())

# 各人の点数を受け取り、リスト A に格納する
A = list(map(int, input().split()))

# 合格者の数をカウントする変数を初期化
ans = 0

# 各人の点数に対して合格基準を満たすか判定し、合格者の数をカウント
for a in A:
    if a >= l:
        ans += 1

# 合格者の数を出力
print(ans)

解き方)

  1. 入力から人数 n と合格基準点 l を受け取ります。
  2. 各人の点数をリスト A に格納します。
  3. 各人の点数に対して、合格基準 l 以上かどうかを判定します。
  4. 合格基準を満たす人の数をカウントして、最終的な結果を出力します。

別解) 内包表記を使ってfor文とif文をまとめています。

# 入力から n(人数)と l(合格基準点)を受け取る
n, l = map(int, input().split())

# 各人の点数を受け取り、その中で合格基準を満たす人数を数えて出力する
print(len([x for x in list(map(int, input().split())) if x >= l]))

B問題:Minimize Abs 1

B – Minimize Abs 1

問題(要約)

長さ N の整数列 A と整数 L, R が与えられたときに、各要素について条件を満たす整数 X を求める。条件は以下の2つです:

  1. LからRの範囲内にある(L≤X_i≤R)
  2. L以上R以下の任意の整数 Y に対して、|X_i – A_i| ≤ |Y – A_i| を満たす

これらの条件を満たす整数 X_i を求めてください。

解答

問題文から、Ai が 整数 L〜R に対して最小になるように設定すれば、Xiが最小になります。

# 入力から整数 n, l, r を取得
n, l, r = map(int, input().split())

# 整数列 A を取得し、リストに変換
A = list(map(int, input().split()))

# 結果を格納する空のリストを作成
ans = []

# 整数列 A の各要素に対して処理を行う
for a in A:
    # 条件に基づいて X_i を求めて結果リストに追加
    if a < l:
        ans.append(l)
    elif l <= a <= r:
        ans.append(a)
    else:
        ans.append(r)

# 結果リストをスペース区切りで出力
print(*ans)

解き方)

  1. 入力の取得:
    • 最初の行で、空白区切りの整数 n, l, r を取得しています。n は整数列 A の長さであり、l と r は条件式に利用される整数です。
    • 次の行で、空白区切りの整数列 A をリストとして取得しています。
  2. 条件に基づいた X_i の計算:
    • for ループでは、整数列 A の各要素 a について以下の条件に基づいて X_i を計算しています。
    • if-elif-else 条件文を使って、a が l より小さい場合は l を、a が l 以上かつ r 以下の場合はそのままの値を、a が r より大きい場合は r を X_i として選択しています。
    • 計算された X_i は ans リストに追加されます。
  3. 結果の出力:
    • print(*ans) を使って、リスト ans の要素をスペース区切りで出力しています。これにより、各要素の X_i が出力されます。

C問題:Minimize Abs 2

C – Minimize Abs 2

問題(要約)

正整数 D が与えられます。

非負整数 x,y に対する x2+y2−D の最小値を求めてください。

解答

import math

# 正整数 D を入力として受け取る
d = int(input())

# x, y の範囲を制限するための side を計算
side = int(math.sqrt(d) + 1)

# 答えを無限大で初期化
ans = float('inf')

# x の範囲を探索
for x in range(side):
    # y の範囲を計算(0 以上の値に制限)
    y_side = int(math.sqrt(max(0, d - x**2)))
    
    # y の範囲を探索
    for y in range(y_side, y_side + 2):
        # 式 |x^2 + y^2 - d| の値を計算し、答えの最小値を更新
        ans = min(ans, abs(x**2 + y**2 - d))

# 最小値を出力
print(ans)


解き方)

  1. 範囲の制約計算:
    • math.sqrt(d)d の平方根を計算し、int を使って整数部分を取得します。これが side となり、xy の範囲を制約するために使われます。
  2. 最小値の初期化:
    • ans = float('inf')ans を無限大で初期化します。これは後で最小値を更新するために使われます。
  3. x の範囲の探索:
    • for x in range(side): で x の範囲を探索します。
  4. y の範囲の計算:
    • y_side = int(math.sqrt(max(0, d - x**2))) で y の範囲を計算します。max(0, d - x**2) は x^2 を引いた残り部分が負にならないようにするためのものです。
  5. y の範囲の探索:
    • for y in range(y_side, y_side + 2): で y の範囲を探索します。y_side から y_side + 1 までの 2 つの値を試すことで、y を探索しています。
  6. 式の計算と最小値の更新:
    • ans = min(ans, abs(x**2 + y**2 - d)) で式 |x^2 + y^2 - d| の値を計算し、これが現在の最小値 ans よりも小さければ ans を更新します。
  7. 最小値の出力:
    • 最終的に print(ans) で最小値を出力します。

感想

今週もC問題まで解けました!B, C問題では苦労しましたが、最終的に解決することができました! ^^ !

B問題では、問題文の理解に苦労しました。何度も問題と例題を繰り返し読み返し、最終的には要点を理解できました。コードの実装は複雑ではありませんでしたが、読解力が試される問題でした。

C問題も難航しました。最初は愚直に全検索で実装しましたが、TLE(制限時間超過)になりました。そこで数学的な観点を用いて検索時間を削減しましたが、特定条件で不正解となりました。最終的には検索幅を一部増やして、すべての場合をカバーできるようにしてやっとAC(正解)になりました。

今回の問題は、B・C問題の難易度が私にとって高かったですが、諦めずに挑戦し、最終的に解答に辿り着けたことが非常に嬉しいです。

Ratingは維持できました(551点)。来週も引き続き頑張ります!

それでは、良い競プロライフを~!(*^▽^)/

コメント

タイトルとURLをコピーしました