茶色コーダーによるPython競技プログラミングへの誘い

この記事は coins Advent Calendar 2019 の14日目の記事です。

13日目の記事は、むらあじによる アドカレポエム でした。

非情報系の方に向けて、Advent Calendar の説明をすると、1つのテーマを決めて、毎年12/1から12/25まで*1の間、参加者がリレー形式でブログでテーマに関する記事を作成するというものです。

9日目には自己紹介と称した気持ち悪いポエムを投稿させていただきましたが、今回はそのポエムでも少し触れた、「競技プログラミング」についての記事を書こうと思います。

しかし、競技プログラミングの情報はかなり充実しており、「競技プログラミング 始め方」などと調べればいくらでも必要な情報は手に入ります。そのため、二番煎じにならないためにも、coins19の「プログラミング入門」でやったことを使ってPython3で競技プログラミングをするまでの流れを解説します。*2

なお、競技プログラミングができるWebサービスはたくさんあるのですが、今回は、AtCoderという日本発のWebサービス競技プログラミングの問題を解くまでの流れを説明します。*3

ちなみに後述するAtCoder Problemsの筆者のデータがこちらになります。見てわかるように初心者です。

f:id:takahero2016:20191214155020p:plain
連続AC記録だけはなんとか続いている

AtCoderの始め方

f:id:takahero2016:20191213134339p:plain
このような画面が出てくると思います(2019/12/13時点)

実際に問題を解いてみよう!

AtCoderには定期的に開催されるコンテストが3種類あります。

  • ABC(AtCoder Beginner Contest:初級者向け)
  • ARC(AtCoder Regular Contest:中級者向け)
  • AGC(AtCoder Grand Contest:上級者向け)

今回はABCのうち最も新しいABC147の問題を解きたいと思います。

ABC147A-Blackjack(クリックすると問題ページに移動)

(下に問題の解説があります)

























問題文を見た感じ、以下の手順が思いつくと思います。

  1. 3個の整数A1, A2, A3をどうにか頑張って受け取る
  2. 3個の整数を足す
  3. 和が22以上かそうでないかを考え、それに応じて出力を変える

2. と3. は、授業で習ったこと(四則演算とif文とprint関数)がそのまま書ければよいのですが、1. は授業で習ったことを応用する必要があります。

授業では、1つの文字列を受け取る方法はやりました。

# 文字列の入力
s = input()

Jupyter Notebookでは、input()を使うと、検索窓みたいな入力フォームが出現して、そこに入力していくことになりますが、もちろんAtCoderで入力するのは自分ではないです。

f:id:takahero2016:20191213141624p:plain
Lesson3の最終問題ならこんな感じ

Python3を使う時には、基本的に入力は1行ずつ受け取ります。例えば

1 2 3

という入力に対して

s = input()

とすると、sは'1 2 3'というよくわからない文字列になってしまい、これでは足し算することはできません。

ここで、split()関数を使います。

s = input().split()
print(s)
>>['1', '2', '3']

split()関数は()の中に書かれた文字(','や'-'など)ごとに分割してくれる組み込み関数ですが、実は、()内に何も入っていなかった場合、入力をスペース区切りしてくれます。

※ちなみに、.split()と.split(' ')の違いはなんだよ!って思うかもしれませんが、実は、.split()は連続するスペースはまとめて1つとして数えます。つまり

1               2                 3

みたいな入力に対しても

s = input().split()
print(s)
>>['1', '2', '3']

と動いてくれます。

これで、とりあえず3つの数字を文字列型で受け取ることができました。
あとは、型変換と四則演算とif文を組み合わせて、以下のようなコードが完成します。

s = input().split()

sum_s = int(s[0]) + int(s[1]) + int(s[2])
if sum_s >= 22:
    print('bust')
else:
    print('win')

ちなみにJupyter-Notebookはprint()を使わなくても最後の行に書かれた変数は中身が見えたりしますが、あれはJupyter-Notebook特有のものであるため、print()は原則必須です。

あとはさっきの問題ページの一番下のソースコードを貼り付ける部分にコピペして、言語がPython3(3.4.3)になっていることを確認してから提出します。

f:id:takahero2016:20191213145328p:plain
提出画面
結果は…
f:id:takahero2016:20191213145510p:plain
Accepted!!
ということで、これでこの問題が解けました。(ACはAcceptedという意味です)

ちなみに、上記のコードではsは文字列型のリストですが、map()関数を使うことでリスト内の型を一気に変えることができます。
また、数値型のリストでは、sum()関数を使うことで総和を計算することができます。
これらを使うと下記のようになります。

# map(引数1,引数2)で引数2の全要素にたいして引数1の操作を行う
s = map(int,input().split())
if sum(s) >= 22:
    print('bust')
else:
    print('win')

ちなみに、上記のコードを三項演算子を使うと以下のように書けて、これでもOKです。

print('bust' if sum(map(int,input().split())) >= 22 else 'win')

次にやること

と、いうわけで、これであなたも競プロerを名乗ることができます!

そして、この後何をすれば良いのか、という話ですが、以下のようなものが考えられます。

  • 主に週末に開かれているコンテストに参加してみる(ホームページ左の「コンテスト」から、現在どのようなコンテストが開催される予定かなどをチェックすることができます)
  • AtCoder Problemsという有志作成のサービスを使って過去問を解く(解いた問題とまだ解いていない問題がわかり、問題を探しやすい)

「がんがんやっていくぞ!」という方は過去問を解きまくるのも良いと思いますし、「まあちょっとずつやろうかな」という方は週末のコンテストにだけでも参加してみると良いと思います。また、コンテストにリアルタイムで参加すると、解けた問題に応じて得点が得られて、得点の総和と解けるまでの速さによってレーティングがつきます。これはいわゆるオンラインゲームみたいな感じで、とても楽しいと思います。ぜひやってみてください!

ではこの辺で終わりにしたいと思います。読んでくださってありがとうございました!



















おまけ

と、言いたいところなのですが、「いやお前授業でやったことを使って、とか言っておきながら結局使ったのif文とinput()だけやろ」と言われてしまいそうなので、ここから自分が競プロの問題を解くにあたって使ったことがある入力と出力について紹介していきます。多分他にも同じことを書いている人はいると思うので、まあ二番煎じ感は否めない*4のですが、自分のアウトプットという意味もかねてここに書いていきたいと思います。

ちなみにここから先の内容は、割と頻繁に更新するかもしれないです*5。あまりにも更新しすぎて分量が増えすぎたら後で別のページを作ると思います。

入力

# 文字列
>> AtCoder
s = input() >> s = 'AtCoder'

# 1文字ずつ分割
>> AtCoder
s = list(input()) >> s = ['A', 't', 'C', 'o', 'd', 'e', 'r']

# 文字(空白区切り)
>> A t C o d e r
s = input().split() >> s = ['A', 't', 'C', 'o', 'd', 'e', 'r']

# 整数(ちなみに競プロでfloat型が入力されたのは少なくとも自分は見たことはない)
>> 1
x = int(input()) >> x = 1

# 複数の整数
>> 1 2 3
x,y,z = map(int,input().split()) >> x = 1, y = 2, z = 3

# 整数(空白区切り)
>> 1 2 3 4 5 6 7 8
A = list(map(int,input().split())) >> A = [1, 2, 3, 4, 5, 6, 7, 8]

# 入力の1文字目だけ別の変数
>> 0 1 2 3 4 5 6 7 8
x,*A = map(int,input().split()) >> x = 0, A = [1, 2, 3, 4, 5, 6, 7, 8]

# 複数行
>>1
2
3
4
5
6
7
8
A = [int(input()) for _ in range(N)] >> A = [1, 2, 3, 4, 5, 6, 7, 8]

# 複数行(1行に複数)
>> 1 2 3 4
5 6 7 8
A = [list(map(int,input().split())) for _ in range(N)]

出力

# 出力
print(s)

# リストを1つずつ出力
A = [1, 2, 3, 4, 5, 6, 7, 8]
for i in range(len(A)):
    print(A[i])

# リストを連結して出力
A = [1, 2, 3, 4, 5, 6, 7, 8]
print(''.join(A)) >> 12345678
print(*A, sep='') >> 12345678

なんか本当はモジュールとか紹介するべきだったんでしょうけど、さすがに5000文字も書くと疲れるのでそれはまた今度にします。ここまで読んでくださりありがとうございました。

*1:クリスマスまでの日数を数えることから

*2:coins19の人間にしかできない独自性を出していきたい…

*3:筆者がCodeforcesTopCoderには手を付けていないため

*4:しかもいろんな記事からかき集めてきた感じ…

*5:Advent Calendarとは…