はじめてのNim(Nim初心者によるAtcoder精選過去問10問)
どーも三十三間党です。皆さんNimという言語をご存知でしょうか。
Nimは爆速で動くPythonです。嘘です。
なないろ言語で競プロまとめ(https://qiita.com/drken/items/6edb1c0542d4c3b7179c)を見ながら、C++ばりの速さが出て、Pythonみたいな書き方できる言語ないかなーと探していたら、2つほど候補が見つかりました。CrystalとNimです。しかしコードを見ていると、やはりCrystalはRubyをリスペクトされて書かれた言語なので、Rubyを学んだことのない僕には少し読みづらかったです。
そこでNimのコードを見るとあらびっくり、これPythonじゃね?と思うほど個人的にはコードが似ていたので,勉強しようと思いました。
今回はそのNimを使って、「AtCoderに登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~」
(https://qiita.com/drken/items/fd4e5e3630d0f5859067)
を解きました。一応Pythonで一回解いたことはあります。
なお、ACしているので、動くコードとは思いますが、Nim初心者(というかAtCoderも初心者)なので冗長だったり、間違った文章を書いたりしている等あると思います。ご了承願います。
今回からはMarkdown方式で書いているので、コードの見づらさは多少改善されているかと思います。
Nimならこういう書き方良いよ!っていうのがあれば教えてください!
1.Welcome to AtCoder
import sequtils, strutils var a = parseInt(readline(stdin)) bc = map(split(readline(stdin)),parseInt) s = readline(stdin) echo (a+bc[0]+bc[1]," ",s
標準入力の()多いかな〜と思いましたが、まあpythonでもこんなもんですよね。空白区切りの標準入力はimport要るんですね。stdinはstandard inputの略か。変数宣言に型は必須ではありませんが、変数の場合varは必須そう。
リストで受け取ってしまいましたが、
var a, b, c: int (a, b, c) = stdin.readLine.split.map(parseInt) という分割代入の方法もあるそうな。
2.Product
import sequtils,strutils var ab = map(split(readline(stdin)),parseInt)<200b> if (ab[0] * ab[1]) mod 2 == 0: echo "Even" else: echo "Odd"
%ではなくてmod。ifの書き方Pythonと一緒ですね。
3.Placing Marble
var S = stdin.readLine var c = 0 for s in S: if s == '1': c += 1 echo c
for s in S(文字列)という書き方もできちゃう。ちなみに文字列はmutableだそうで。
4.Shift Only
import strutils import sequtils var N = stdin.readline.parseint A = stdin.readline.split.map(parseint) ans = 0 block myblock: while true: for i in 0..N-1: if A[i] mod 2 != 0: break myblock A[i] = int(A[i]/2) ans += 1 echo ans
なんか急に標準入力受け取る書き方変えてしまいました(やってることはおそらく同じですが)。なんか()の部分を.で書き換えられる?らしい。
block文が個人的には新しい。最初に自分でblockを作っておくと、breakでどのblockから抜けるか選べます。多重ループ 抜け方 でググることもなくなりますね。
while文も書き方同じですが、Trueではなくてtrueですね。ちなみにNimは大文字小文字を区別しないようですが、最初の一文字目だけ区別するようです。
for文の中の、0..N-1という書き方で、Pythonのrange(N)と同じみたい。
/は何があろうとfloat型返すらしく、int型に変換しないと元の変数に代入することができません。そんなもんか。
2進法に書き換えて云々で解きたかったのですが2進数への書き換えが意外と面倒くさい?のかも
5.Coins
import strutils var A = stdin.readline.parseint B = stdin.readline.parseint C = stdin.readline.parseint X = stdin.readline.parseint ans = 0 for a in 0..A: for b in 0..B: for c in 0..C: if 500*a + 100*b + 50*c == X: inc(ans) echo ans
ans += 1はinc(ans)でもおっけー。今まで全部実行時間1msですね。
6.Some Sums
import sequtils, strutils, math var N,A,B:int ans = 0 (N,A,B) = stdin.readline.split.map(parseint) for i in 1..N: var c = 0 x = i while x > 0: c += x mod 10 x = int(x/10) if A <= c and c <= B: ans += i echo ans
気づいてませんでしたが、
import ho import ge
を
import ho, ge
という書き方もできるようです。
Pythonで書いていたときは文字列リストに変換してから整数に変換し、その和を取るという解き方をしていたので、その書き方をしようとしたら思った以上に面倒くさかったのでやめました。型を変えずに処理できるときはそうしたほうが良さそうかも。
7.Card Game for Two
import strutils import sequtils import algorithm proc getInt() : int = parseInt(readLine(stdin)) proc getInts() : seq[int] = readLine(stdin).split().map(parseInt) var n = getInt() a = getInts() ans = 0 a.sort(cmp,Descending) for i in 0..<n: if i mod 2 == 0: ans += a[i] else: ans -= a[i] echo ans
標準入力長いなーと思っていたら(普通の長さかもしれませんが)、関数にしてまとめている方がいらっしゃったので真似させていただきました。
sortのDescendingで降順に並べ替えて(引数cmpはよくわかりませんでした)解きました。
8.Kagami Mochi
import strutils import sequtils import sets proc getInt() : int = parseInt(readLine(stdin)) var n = getInt() d = initset[int](8) for i in 1..n: d.incl(getInt()) echo d.card
集合の要素数を数えます。initsetintで2の冪乗サイズの整数型が入る集合になるようです。
9.Otoshidama
import strutils import sequtils var ny = stdin.readline.split.map(parseint) n = ny[0] y = ny[1] block myblock: for i in 0..n: for j in 0..n-i: if i * 10000 + j * 5000 + (n-i-j) * 1000 == y : echo (i," ",j," ",n-i-j) break myblock echo("-1 -1 -1") 実行時間が3msになりました。
個人的には3重ループ間に合わねーぜ!っていう問題だと思っていたのですが、
import strutils import sequtils var ny = stdin.readline.split.map(parseint) n = ny[0] y = ny[1] block myblock: for i in 0..n: for j in 0..n-i: for k in 0..n-i-j: if i * 10000 + j * 5000 + k * 1000 == y and i+j+k == n: echo (i," ",j," ",k) break myblock echo("-1 -1 -1") 3重ループ間に合っちゃった……(実行時間905ms)速さは正義。
10.白昼夢/Daydream
import strutils,unicode var s = readline(stdin) words = ["eraser","dreamer","erase","dream"] while true: for word in words: removesuffix(s,word) if s == "": echo "YES" break else: if not endswith(s,words[0]) and not endswith(s,words[1]) and not endswith(s,words[2]) and not endswith(s,words[3]): echo "NO" break
速え(当社比、実行時間3ms)。文字列操作強力ですね!
11.Traveling
import strutils,sequtils var N = stdin.readline.parseint for i in 1..N: var t,x,y:int (t,x,y) = stdin.readline.split.map(parseInt) if x + y > t or (((x+y) mod 2) != (t mod 2)): echo "No" quit() echo "Yes"
実行時間59msと最長の結果になったのですが、他の人の提出結果も見たところ、こんなもんかと思われますが,Nimの最速は11msでした。
感想 速い、Pythonっぽい、好き(小並感)