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

abc328 AtCoder

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

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

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

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

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

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

AtCoder Beginner Contest 328

A問題:Not Too Hard

A – Not Too Hard

問題(要約)

プログラミングコンテストで出題されるN問の問題の各配点が与えられます。各問題の配点がX以下である場合、それらの問題の配点の合計を出力してください。

解答

# 入力から問題数とXの値を取得
n, x = map(int, input().split())

# 各問題の配点をリストで受け取る
S = list(map(int, input().split()))

ans = 0
# 配点がX以下の問題の合計を計算
for s in S:
    if s <= x:
        ans += s

# 結果を出力
print(ans)

解き方)

  1. 最初に、問題の数(n)とXの値を受け取ります。
  2. 次に、各問題の配点をリストとして受け取ります。
  3. ans という変数を用意し、初期値を0に設定します。これは最終的な合計値を保持するための変数です。
  4. for ループを使用して、各問題の配点に対して以下の操作を行います。
    • もし問題の配点がX以下であれば、その配点を ans に加算します。
  5. ループが終了したら、計算された ans を出力します。

別解)内包表記を使用

# 入力から問題数とXの値を取得
n, x = map(int, input().split())

# 各問題の配点をリストで受け取り、条件を満たすものの合計を出力
print(sum(v for v in list(map(int, input().split())) if v <= x))

B問題:11/11

B – 11/11

問題(要約)

AtCoder 国のカレンダーは1年がNか月で構成されており、各月がそれぞれD日間からなります。ゾロ目の日とは、i月j日が1種類の数字だけを用いてiとjを十進法で表すことができる日付のことを指します。与えられた条件のもとで、1年の中でゾロ目の日が何日あるかを求めてください。

解答

n=int(input())  # 月の数
D=list(map(int,input().split()))  # 各月の日数リスト

ans=0  # ゾロ目の日の総数

# 各月について
for i in range(1, n+1):
    m = str(i)  # 月を文字列に変換

    # 月がゾロ目になる条件を満たす場合
    if m.replace(m[0], "") == "":
        # その月の日について
        for j in range(1, D[i-1]+1):
            # 日がゾロ目になる条件を満たす場合
            if str(j).replace(m[0], "") == "":
                ans += 1

print(ans)

解き方)

  1. まず、各月の日数が D として与えられています。この日数を利用して、各月について以下の処理を行います。
  2. for i in range(1, n+1):: 1 から N までの各月について、ゾロ目になる条件を満たすかを判定します。
  3. m = str(i): 月を文字列に変換します。これは、月がゾロ目になる条件を判定するためです。
  4. if m.replace(m[0], "") == "":: 月がゾロ目になる条件を満たす場合、その月の日について以下の処理を行います。
  5. for j in range(1, D[i-1]+1):: 1 からその月の日数までの各日について、日がゾロ目になる条件を満たすかを判定します。
  6. if str(j).replace(m[0], "") == "":: 日がゾロ目になる条件を満たす場合、ans(ゾロ目の日の総数)をインクリメントします。

C問題:Peak

C – Peak

問題(要約)

与えられた文字列 S に対して、複数のクエリが与えられます。各クエリは、指定された部分文字列において同じ英小文字が隣り合っている箇所の数を求めます。各クエリに対する答えを順番に出力してください。

解答

# 入力
n, q = map(int, input().split())
S = input()

# 隣り合う同じ文字の数を累積的に計算するリスト
consecutive_S = [0] * n

# Sの各文字について、隣り合う同じ文字があれば累積を増やす
for i in range(1, n):
    if S[i - 1] == S[i]:
        consecutive_S[i] = consecutive_S[i - 1] + 1
    else:
        consecutive_S[i] = consecutive_S[i - 1]

# 各クエリに対して、累積リストから範囲内の同じ文字の数を計算して出力
for _ in range(q):
    l, r = map(int, input().split())
    print(consecutive_S[r - 1] - consecutive_S[l - 1])


解き方)

  1. この問題では、与えられた文字列 S について、隣り合う同じ文字がいくつあるかを事前に計算し、質問が来た際にその情報を使って効率的に答えます。
  2. まず、consecutive_S リストを用意します。このリストは S の各文字について、その文字までの部分文字列において隣り合う同じ文字の数を累積的に記録したものです。
  3. 例えば、S = "abcaab" の場合、consecutive_S = [0, 0, 0, 1, 1, 1] となります。これは、S[0:1] までの部分文字列に同じ文字の隣り合いはない、S[0:2] までの部分文字列に同じ文字の隣り合いはない、…、S[0:4] までの部分文字列には1箇所同じ文字の隣り合いがある、… といった具体的な情報を表しています。
  4. 次に、各質問に対して l から r の範囲内で同じ文字の隣り合いの数を求めます。これは、consecutive_S[r-1] - consecutive_S[l-1] として計算できます。これは l から r-1 までの部分文字列において同じ文字の隣り合いの数を表しているので、その差をとることで l から r-1 の範囲内の同じ文字の隣り合いの数を得ることができます。
  5. このアプローチにより、同じ文字の隣り合いの数を事前に計算しておき、質問に対してはその情報を用いて瞬時に答えることができます。

感想

今週もC問題まで解けました!最近、安定して嬉しいです !^^!

B問題は、数字⇔文字列変換を行い、replace機能を使えば解けると思い実装しましたが、例題の回答が合わずに焦りました。桁数部分の処理ができていないことが判明し、一桁目と二桁目の判定も導入して回答まで辿り着きました。

C問題は、初めはスライス機能を用いてクエリ毎に連続文字を探したのですが、制限時間超過で不正解。少し予想はしつつ、次の手を考えました。for文の回す回数を少なくするため、元の配列において連続文字を探し累積和で考え、無事回答まで辿り着きました。

この後、D問題に挑戦するも、今週も解けずでした・・・。C問題まで安定して解けてきたので、D問題を解けたと報告できる日を夢見て引き続き精進していきます。

Ratingは若干下がってしまいましたが、自身の最高点に近い559点なので、来週も頑張りたいと思います。

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

コメント

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