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

AtCoder

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

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

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

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

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

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

AtCoder Beginner Contest 349

A問題:Zero Sum Game

A – Zero Sum Game

問題文

1 から N の番号が付けられた N 人の人がおり、この中で一対一の勝敗のつくゲームを何度か行いました。N 人は最初にそれぞれ持ち点として 0 を持っており、各ゲームでは勝者の持ち点が 1 増え、敗者の持ち点が 1 減ります(持ち点が負になることもあります)。最終的に人 i (1 ≤ iN−1) の持ち点が Ai になったとき、人 N の持ち点を求めてください。なお、ゲームの進行によらず最終的な人 N の持ち点は一意に定まることが示せます。

解答

n = int(input())  # プレイヤーの総数Nを入力から読み込む
# 入力されたN-1個のスコアを読み取り、整数リストに変換し、その合計を計算して符号を反転して出力
print(-sum(list(map(int, input().split()))))

考え方)

この問題における核心は、全てのゲームの結果による得点の総和が常にゼロであるという点です。つまり、全プレイヤーの得点の合計はゲームが始まる前後で変わりません(ゼロのままです)。

具体的に説明すると、以下のようになります:

  1. 各ゲームでは勝者が1ポイントを得て、敗者が1ポイントを失います。このため、各ゲームで得点の合計変化は0です(+1と-1の合計)。
  2. この問題では、1人からN-1人目までのプレイヤーの最終得点が与えられます。これらの得点の合計を求めると、N人目を除いた全プレイヤーの得点の合計が出ます。
  3. ゲームの性質上、全プレイヤーの得点の総和は0でなければならないため、N人目のプレイヤーの得点は、他のプレイヤーの得点合計の逆数(符号が反対)である必要があります。

つまり、1人目からN-1人目までの得点の合計を求めて、その数値の符号を反転させれば、N人目のプレイヤーの得点が求まります。これは、N人目の得点が他の全プレイヤーの得点合計を打ち消す値でなければならないためです。

コード詳細

  1. n = int(input()):ユーザーからプレイヤーの総数Nを入力として受け取ります。
  2. input().split():次の行でN-1個のスコアを入力として受け取り、スペースで区切られた文字列をリストに変換します。
  3. map(int, input().split()):上記のリストの各要素(スコア)を整数に変換します。
  4. list(map(int, input().split())):整数に変換されたスコアのリストを作成します。
  5. sum(list(map(int, input().split()))):整数のリストの合計値を計算します。
  6. -sum(list(map(int, input().split()))):合計値の符号を反転します。これは、N番目のプレイヤーのスコアが残りのプレイヤーのスコアの総和に反対の符号で等しいことを表します。
  7. print(-sum(list(map(int, input().split())))):計算したN番目のプレイヤーのスコアを出力します。

B問題:Commencement

B – Commencement

問題

英小文字からなる文字列 S が良い文字列であるとは、すべての 1 以上の整数 i について次の性質が成り立つことであるとします。

  • S にちょうど i 回現れる文字はちょうど 0 種類またはちょうど 2 種類ある

文字列 S が与えられるので、 S が良い文字列か判定してください。

解答

from collections import Counter  # 文字の出現回数をカウントするためのモジュールをインポート

S = input()  # ユーザーから文字列 S を入力として受け取る

characters = Counter(S)  # 文字列 S の各文字の出現回数をカウント

NumLetter = [0] * 101  # 各出現回数に対する文字の種類数を格納するリスト(0から100回までを想定)

for cnt in characters.values():  # 各文字の出現回数について
    NumLetter[cnt] += 1  # 出現回数に応じてリストの対応するインデックスの値を1増やす

# NumLetterリストの各要素が0か2であるかどうかをチェック
if all(x in (0,2) for x in NumLetter):  
    print("Yes")  # すべての要素が0か2なら"Yes"を出力
else:
    print("No")   # それ以外の場合は"No"を出力

ポイント)

下記の順番で解法を行なっています。

  1. 文字列の出現回数をカウントします。
  2. 出現回数の数をカウントします。例 i=1ot の 2 種類 の2つ部分を数えにいきます。
  3. 出現回数の数が、0, 2 か それ以外かを判定します。

コード詳細

  1. from collections import CounterCounter クラスをインポートします。これは文字の出現回数をカウントするのに便利です。
  2. S = input() でユーザーから文字列 SS を受け取ります。
  3. characters = Counter(S) を用いて、文字列 SS 中の各文字の出現回数をカウントし、characters という辞書に格納します。
  4. NumLetter = [0] * 101 で、出現回数 ii ごとにその回数で出現する文字の種類数をカウントするリストを初期化します。
  5. for cnt in characters.values():characters 辞書の各値(出現回数)についてループを行います。
  6. NumLetter[cnt] += 1 で、特定の回数 cntcnt で出現する文字の種類数をカウントアップします。
  7. if all(x in (0,2) for x in NumLetter): で、NumLetter の各要素が0か2のいずれかであるかを確認し、すべて該当する場合に “Yes” を出力します。
  8. それ以外の場合は “No” を出力します。

C問題:Airport Code

C – Airport Code

問題

英大文字からなる長さ 3 の文字列 T が、英小文字からなる文字列 S の 空港コード であるとは、 T が S から次のいずれかの方法により得られることとします。

  • S の長さ 3 の(連続とは限らない)部分列をとり、それを英大文字に変換したものを Tとする
  • S の長さ 2 の(連続とは限らない)部分列をとり、それを英大文字に変換したものの末尾に X を追加したものを T とする

文字列 ST が与えられるので、 T が S の空港コードであるか判定してください。

解答

S = input().upper() + "X"  # 入力された文字列 S を大文字に変換し、末尾に 'X' を追加
T = input()  # 入力された文字列 T を受け取る

j = 0  # T の文字列を検索するためのインデックス

for s in S:  # 変換された S の各文字に対してループ
    if s == T[j]:  # 現在の文字 s が T の現在の文字と一致する場合
        j += 1  # インデックスを1つ進める
    if j == 3:  # インデックスが 3 に達した場合
        exit(print("Yes"))  # プログラムを "Yes" と出力して終了
print("No")  # ループが完了しても条件を満たさなかった場合 "No" を出力

ポイント)

下記のポイント

  • 文字列Sを大文字に変換し、末尾に X を追加します。
    末尾に X を追加することにより2文字の場合分けが不要になります。
  • 文字列Tの文字の現在位置を j で表します。
  • 文字列Sの文字を一つづつ確認し、文字列 T[j] が見つかった場合に j に1を加算していきます。

解き方(詳細):

  1. S = input().upper() + "X" で、ユーザーから入力された文字列 SS を大文字に変換し、末尾に ‘X’ を追加します。これにより、TT が2文字の部分列に ‘X’ を追加した形である場合も検討できるようになります。
  2. T = input() で、ユーザーから目標とする文字列 TT を受け取ります。
  3. j = 0 で、TT の各文字を検索するためのインデックスを初期化します。
  4. for s in S: で、変換後の SS の各文字 ss に対してループを行います。
  5. if s == T[j]: で、現在の文字 ss が TT の現在のインデックス jj の文字と一致するかを確認します。一致する場合は j += 1 でインデックスを進めます。
  6. if j == 3: で、インデックス jj が3に達しているかを確認します。3に達した場合は、TT の全文字が SS から見つかったことを意味し、exit(print("Yes")) で “Yes” を出力してプログラムを終了します。
  7. print("No") は、ループが終了し jj が3に達しなかった場合に実行されます。これは、TT が SS の空港コードとして成立しないことを意味します。

感想

今回はC問題まで解けました。

A問題は、問題の意図を捉えるのが難しく、A問題からつまづいたことでの焦りも相まって、少しパニックになりました。A問題だからと自分に言い聞かせ、問題の意図をしっかり捉えることで落ち着きを取り戻しました。与えられたリストの合計を計算し、その符号を反転することで問題を解くことができました。

B問題は、文字カウントと出現回数が問題文に指定している内容になっているかを確認する問題でした。与えられた制約が大きくなかったため、与えられた問題通りに実装していくことで解くことができました。

C問題は、部分文字列を判断する問題でしたが、初めは部分文字列の意味を完全には理解しておらず、文字が含まれているかだけを確認してしまいました。順序も考慮する必要があるため、序数を条件にあったときだけ増やすことにより対応することができました。

C問題まで解答できたことにより、レーティングは横ばいの540でした。来週もこのエキサイティングな挑戦を楽しんで続けたいと思います。

それでは、皆さんも競技プログラミングを楽しんでくださいね!(*^▽^)/

コメント

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