【Python3】競技プログラミングの回答方法がわからないときに見るもの
研修でJavaばっかりやってるので全然Pythonの勉強ができない。
いや時間はあるんですが、いかんせん体力がね...
ジム通いでも始めたいところですが、今度はジムの会費が高すぎて払えないという。貧乏社会人のつらいところです。
さて、今回はPython初心者の方向けの記事を書きます。
Pythonの基本的な文法は入門書や学習サイトで勉強したし、いっちょ競技プログラミングで腕試しするか!という方が、陥りがちな落とし穴があります。
paizaとかCodeIQとか、そのへんは難易度的にもとっつきやすいんですが、この落とし穴を乗り越えない限り、解法は分かるのになぜか不正解になる...ということになってしまいます。
・標準入力
よくあるつまづきポイントがこちら。
Nが与えられ、それをうんたらかんたらした結果を出力して下さい。 なお、Nは標準入力から渡されます。
標準入力?
なにいってんだこいつ。
この言葉を初めて見た方はこう思うことでしょう。
私もこれが分からず不正解を出し続け、ついに「与えられた整数をすべて足し合わせた数を出力してください」なんていうクッソ簡単な問題で時間切れになってしまい、ショックでその後3日間パソコンに触らなかった悲しい過去があります。
標準入力を受け取る
これは要は「キー入力を受け付ける」操作をして値を受け取ってね、ということなのです。
input()関数を使いましょう。
>>> n = input()
これで変数nに入力された値、さっきの問題でいうところのNが代入されます。簡単だな!
なお、input()で得られた値は問答無用でstr型になるので、計算問題の場合は型変換しましょう。
>>> # 1を入力 >>> n = input() >>> print(n, type(n)) 1 <class 'str'>
>>> # 1を入力 >>> n = int(input()) >>> print(n, type(n)) 1 <class 'int'>
スペース区切りの標準入力
変則パターンとして、「スペースで区切られたN、Mが与えられる」というものもあります。
input()は与えられた入力を一行まるまる取得するので、行の中で要素を区切りたいときはinputに加えひと手間必要になります。
split()関数を使います。
>>> # 「1 2」を入力 >>> n = input().split(" ") >>> print(n) ['1', '2']
これでsplit()で指定された文字列で区切られた値を、配列に格納された形で得ることができました。
「指定された文字列」なので、例えばカンマで区切られているときはinput(",")とすることもできます。
配列の型変換
ここで注意したいのが、int()型変換は配列に対して使うことができないということです。
さっきと同じノリで型変換をしようとするとエラーを出します。
>>> # 「1 2」を入力 >>> n = int(input().split(" ")) int() argument must be a string, a bytes-like object or a number, not 'list'
>>> # 「1 2」を入力 >>> n = int(input()).split(" ") invalid literal for int() with base 10: '1 2'
※ 配列にする前に型変換をしようとしても、文字列(スペース)を含む値をintに変換することはできません。
配列の要素を型変換したい場合は、map()関数を使いましょう。
>>> # 「1 2」を入力 >>> n = input().split(" ") >>> n_int = list(map(int,n)) >>> print(n_int) [1, 2]
mapとlistという見慣れない関数が出てきましたが、一つ一つ解説していきます。まずmap()関数から。
map関数は、第一引数(intの部分)に任意の関数を、第二引数(nの部分)以降に任意の配列を指定することで、「配列の要素全てに、指定した関数の処理を加える」というものです。
応用範囲が広そうでゾクゾクしませんか?
つまり、前述のコードは、このコードと同じことをしていることになります。
>>> # 「1 2」を入力 >>> n = input().split(" ") >>> for i in range(len(n)): ... n[i] = int(n[i]) >>> print(n)
先ほど、配列全体に対してint()をかけることができないことを確認しました。
ならば、配列の要素ひとつひとつはただのstr型なので、それらひとつひとつにint()をかけてやればいいじゃん、ということです。
次にlist()関数ですが、これは引数を配列型に変換する関数です。int()やstr()の仲間ですね。
なぜこの関数が必要かというと、map()関数は引数に配列を要求しておいて戻り値にはイテレータを返す、というとんだアナーキストだからです。
戻り値として返されたイテレータを、もう一度配列型に変換する必要があるんですね。
>>> # 「1 2」を入力 >>> n = input().split(" ") >>> n_iter = map(int,n) >>> n_int = list(map(int,n)) >>> print(n_iter) >>> print(n_int) <map object at 0x7fb604c48518> [1, 2]
map()関数は便利なので覚えておくといいでしょう。
・標準出力
さて、無事標準入力を受け取り、アルゴリズムを生み出し、みごと配列型に格納された回答を導き出すことができました。あとはprint()関数を使い回答を出力するだけです。
問題を見ると、「スペースで区切って一行で出力してください」とあります。
改行をなくすならend = "" を使えばいいな...せや!
>>> results = [1, 2, 3] >>> for i in results: ... print(i, end = " ") 1 2 3
これだと不正解になります。なんでだよ!
わかりやすいように、値を出力した後に"|"を入れてみましょう。
>>> results = [1, 2, 3] >>> for i in results: ... print(i,end = " ") ... print("|",end = "") 1 |2 |3 |
うまく伝われば幸いですが、この回答だと3の後にスペースが入っていますね。
問題の指定は「スペースで区切る」なので、区切る必要のない最後の要素の後についているスペースは不要なスペースと判定されます。
これこそが、陥りがちな落とし穴、「余分なスペース」です。
計算結果がint型やstr型だけ、という問題なら大丈夫ですが、計算が複雑になってくると配列を使う場面がでてきます。
そんなときにfor文をまわして要素を出力すると、このように余分なスペースも一緒についてきてしまいます。
余分なスペースを出力しない
じゃあどうするかというと、join()関数を使いましょう。
>>> results = [1, 2, 3] >>> results_int = map(str, results) >>> answer = " ".join(results_int) >>> print(answer) 1 2 3
join()関数は、指定した文字列を、指定した配列の要素の間に割り込ませて一つの文字列にする、という処理をします。
「要素の間」なので、間の無い最後の要素にスペースが入る心配はありませんね。
join()はstr型の配列にしか使えませんので、map()関数を使いresults配列の要素をstr型に変えてから使います。
これで余分なスペースの無い、すっきりした文字列を出力することができました。
map()やjoin()を知らないと、初期の私のようにこんなクソコードを書く羽目になってしまうので、知らなかった方はぜひ覚えていってください!
>>> results = [1, 2, 3] >>> for i in results: ... print(i,end = "") ... if i != results[len(results)-1]: ... print(" ",end = "") 1 2 3
入力と出力をマスターして、良い競技プログラミングライフを!