Atcoder Beginners Selectionを難解言語Pietで解いてみた

いきさつ

最近、Atcoder Beginners Selection をなんらかのプログラミング言語で解いてブログに上げる行為が流行っているようなので、Pietで流行に乗っていくことにした。

Pietとは

難解プログラミング言語の一つで、ソースコードがドット絵であることが特徴。慣れてくればソースコードフローチャートに見えてくるのでその点においては画期的(だと信じてる)。詳しいことは「Piet」で検索してください。

AtcoderではPietのソースコードを提出できないので、手元でPietのc++インタプリタに画像データを埋め込んで提出している。

A はじめてのあっとこーだー

実際に提出したc++コード

f:id:basemusi:20180321184452p:plainf:id:basemusi:20180321184512p:plain
AのPietコードと説明付きコード

A問題からループを使っていく必要がある。文字列の入力は1文字ずつ入力を受け取って、その文字が改行かチェックしながら行う。Pietはプログラミングが何も分からなくても、ぐるぐるしてそうな場所が分かるよい言語。

B Product

実際に提出したc++コード

f:id:basemusi:20180321185501p:plainf:id:basemusi:20180321185506p:plain
BのPietコードと説明付きコード

左上の部分で掛け算をして、右上と右下に分岐してそれぞれ出力する。分岐が実際に分岐している。

C Placing Marbles

実際に提出したc++コード

f:id:basemusi:20180321185812p:plainf:id:basemusi:20180321185818p:plain
CのPietコードと説明付きコード

1の数を数えてmod演算を使う。ソースコードの図形的には簡単。

D Shift only

実際に提出したc++コード

f:id:basemusi:20180321190116p:plainf:id:basemusi:20180321190123p:plain
DのPietコードと説明付きコード

最初に暫定解をINFにしておく。N個の数それぞれについて、最大何回2で割れるかを右上のループで求めて、その数と暫定解のminを暫定解にする。ループしてる感じがコードからにじみ出てる。

E Coins

実際に提出したc++コード

f:id:basemusi:20180321190711p:plainf:id:basemusi:20180321190715p:plain
EのPietコードと説明付きコード

11問中最も難しい。高等テクニックである三重ループを要求されるが、気合いを出すと実装できる。実装したときの美しさは他の言語を圧倒する。

F Some Sums

実際に提出したc++コード

f:id:basemusi:20180321191042p:plainf:id:basemusi:20180321191049p:plain
FのPietコードと説明付きコード

ループと分岐を実装していくと描ける。この辺のプログラムは見た目がフローチャートっぽい。

G Card Game for Two

実際に提出したc++コード

f:id:basemusi:20180321191713p:plainf:id:basemusi:20180321191719p:plain
GのPietソースコードと説明コード

上半分でバブルソートをして、下半分で貪欲に大きい数が書かれたカードをとっていく。ソートの実装は重いが、Pietはソースコードの再利用も可能なので、既に描いてあるソートプログラムをコピーすると簡単。

H Kagami Mochi

実際に提出したc++コード

f:id:basemusi:20180321192228p:plain
f:id:basemusi:20180321192234p:plain
HのPietコードと説明付きコード

長さ100超の配列を用意して(左のループ)、各d_iについて配列のd_i要素目をインクリメントし(中央のループ)、配列の中の1以上の数がいくつあるかを数える(右のループ)。1以上の判定にはnot演算を使うとよい。greater演算を使うよりもスリムに書ける。

I Otoshidama

実際に提出したc++コード

f:id:basemusi:20180321192931p:plainf:id:basemusi:20180321192941p:plain
IのPietコードと説明付きコード

1万円札A枚、5000円札B枚、1000円札C枚とすると、C=N-A-Bとなるので、AとBのループだけでよい。しかし残念なことに、自分のPietインタプリタが定数倍遅すぎてこの回答だとTLEしてしまった。悲しい。最高のPietインタプリタコンパイラでもいいよ)求む!!!

※3/21 20:53追記

よく考えると5000円のループは8枚以下しか回さなくてよいという情報が降ってきたのでO(N)で書き直してACした。

f:id:basemusi:20180321205055p:plainf:id:basemusi:20180321205104p:plain
IのPietコードと説明付きコード修正版

J 白昼夢 / Daydream

実際に提出したc++コード

f:id:basemusi:20180321193410p:plainf:id:basemusi:20180321193417p:plain
JのPietコードと説明付きコード

この問題では文字列を後ろから見ていった方が実装が簡単だが、Pietはメモリがスタックしかないので、何も考えなくても後ろから処理したくなる。しかし、Pietでは大きな定数を打ち込む気にあまりならないので(不可能ではないが不格好になる)文字のeなどをプログラムで使うたびに10*10+1=101='e'というようなパズルをすることになって実装がしんどい。

Pietでは分岐処理の間に黒い線を入れて分岐している感じを際立たせたりも可能。

K Traveling

実際に提出したc++コード

f:id:basemusi:20180321194033p:plainf:id:basemusi:20180321194040p:plain
KのPietコードと説明付きコード

時間差dt, x座標の差dx, y座標の差dyについてdt >= dx + dy && (dt - dx - dy) % 2 == 0が成り立つことを確認すればよい。

dxとdyの絶対値をとる部分は関数化して、まったく同じ絵柄を用いている。関数化すると便利。

まとめ

11問全問をPietで解くことができた。したがって、Pietでプログラミングコンテストに出てもよいというお墨付きが得られた(と思う)。みなさんも難解言語でプログラミングコンテストに参加していってください(僕はしません)。