LoginSignup
9
6

More than 3 years have passed since last update.

AtCoder に登録したら解くべき精選過去問 10 問を Julia で解いてみた

Last updated at Posted at 2020-08-31

はじめに

AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~ で紹介されていた問題をJuliaで解いてみました。

訂正やよりわかりやすいコードがあればぜひ教えて下さい。

なお、投稿する時点でのAtCoder上のJuliaはver1.4.0です。
アップデートにより内容に齟齬が生じる可能性があります。
(特に後述する ABC086C - Traveling のスコープ関連で言語仕様が変わる可能性が高いです)

対象

・Juliaの文法がわからない方。
・Juliaのコードの雰囲気を優しい問題と見比べながら掴みたい方。

第1問:ABC086A - Product

解答例1

A,B=parse.(Int,split(readline()))
if A*B%2==0
  println("Even")
else
  println("Odd")
end

解説

readline()は標準入力から一行を読み込み、split()は文字列を空白区切りで分割します。
つまりsplit(readline())["a", "b"]です。

parse(Int,string)stringInt型に変換します。
関数を配列の値全てに作用させるとき、Juliaでは関数.(配列)という書き方ができます。
(殆どの言語では高階関数mapとして実装されているのではないでしょうか。もちろんJuliaにもあります。)

つまり、parse.(Int,["a", "b"])は2つの整数を要素に持つ配列[a, b]となります。
a,b=[a, b]として、それぞれ変数に代入しif節で場合分けしています。

結果を計算したら、println()で出力します。
print()でも標準出力ができますが、末尾に改行が入らないためprintln()を用いるほうが無難でしょう。

解答例2

println(iseven(prod(parse.(Int,split(readline())))) ? "Even" : "Odd")

解説

prod()は配列の要素をかけ合わせる関数でprod([a, b])a*bを返します。
iseven()は偶数か否かを判定します。
iseven(...) ? "Even" : "Odd"は三項演算子です。
Bool値 ? trueの場合 : falseの場合というように評価を分岐できます。

第2問:ABC081A - Placing Marbles

解答例

println(count(isequal('1'),readline()))

count(関数,配列)は配列の要素に関数を作用させた際のtrueを数えます。

isequal(a,b)abが等しいか否かを判定します。
isequal(a)aと引数が等しいか否かを判定する関数を返します。

count(isequal('1'),readline())は文字列sの要素である文字がそれぞれ'1'と等しい回数を数え上げます。

第3問:ABC081B - Shift only

解答例

f(x) = iseven(x) ? 1 + f(x ÷ 2) : 0
println(minimum(f.(parse.(Int,split(readlines()[2])))))

解説

一行目では関数を定義しています。
f(x)xを受け取ると、2で割り切れるなら1+f(x/2)を、そうでなければ0を返します。
x = 2 ^ n * odd のとき、f(x)=1+1+1+1+.....+1+f(odd)となり、f(odd)0なので、nを返します。

readlines()は改行区切りで全ての行を読み込みます。
Juliaの配列は1番目から始まるため、readlines()[2]で2行目を指定できます。

minimum()は配列の最小の値を返します。

第4問:ABC087B - Coins

解答例

A,B,C,x=parse.(Int,readlines())
println(count([500a+100b+50c==x for a=0:A,b=0:B,c=0:C]))

[関数 for i=itr1, j=itr2... if Bool値]は内包表記です。
iitr1 の要素それぞれ、jitr2の要素それぞれとなる繰り返しにおいて、
Bool値trueのときの関数を要素に持つ配列です。
凡例のif以降は必要ないので省略されています。

begin:endbeginから1ずつ増えてendまでを要素に持つ配列です。
begin:step:endbeginからstepずつ増えてendまでを要素に持つ配列です。

500 * a500aと表記することができます。

第5問:ABC083B - Some Sums

解答例

n,a,b=parse.(Int,split(readline()))
println(sum([i for i=1:n if a <= sum(parse.(Int,[string(i)...])) <= b]))

解説

内包表記内のifが省略されず出てきました。

ifの中にあるstring()は数字を文字に変換します。
...は配列をバラバラにします。
sum()は引数を全て足した数を返します。

つまりsum(parse.(Int,[string(i)...]))iを文字列に変換し、要素(文字)に分け、
その後全てを整数に読み替えて足し合わせます。

不等号はa <= X <= bと連続的に書くことができます。

第6問:ABC088B - Card Game for Two

解答例

n=parse(Int,readline())
a=sort(parse.(Int,split(readline())))
println(sum([a[i]*(-1)^(n-i) for i=n:-1:1]))

解説

sort()は配列を昇順に並べた配列を返します。
n:-1:1nから始まって-1ずつ増える最後が1の配列です。
後手が取得した数字の分だけ有利が減るとして、後手の取る数字に-1を掛けて足しています。

第7問:ABC085B - Kagami Mochi

解答例

println(length(unique(readlines()[2:end])))

解説

unique()は配列から重複を取り除いたものを返します。
length()は配列の要素数(長さ)を返します。
配列[end]は配列の最後の値を指します。
配列[2:end]のようにスライスにもできます。
標準入力を改行区切りにしたリストの2つ目以降から重複を削除したものの長さを出力しています。

第8問:ABC085C - Otoshidama

解答例

n,y=parse.(Int,split(readline()))
ansvec=[[a,b,n-a-b] for a=0:n, b=0:n if 10a + 5b + n-a-b == y / 1e3 && b <= n-a]
ans=[ansvec; [-1,-1,-1]][1]
join(stdout,ans," ")

解説

1e31*10^3の正規形です。
ansvecは条件を満たす[a,b,c]の組を要素に持つ配列です。

[配列;配列]は配列同士を結合します。
ansvecに要素が存在しないとき、[ansvec; [-1,-1,-1]][1][-1,-1,-1]であり
それ以外の場合はもともと存在した条件を満たす[a,b,c]の組を指します。

join(io,配列,区切り)配列の要素の間を区切りで埋めてioに出力します。
stdout(標準出力)を指定すればprintを省略できます。

第9問:ABC049C - 白昼夢

解答例

println(occursin(r"^(erase(r)?|dream(er)?)*$",readline()) ? "YES" : "NO")

解説

r"正規表現"で正規表現のパターンを表すことができます。
occursin(パターン、文字列)でパターンにマッチする部分が含まれるかどうかを判定することができます。

第10問:ABC086C - Traveling

解答例

function main()
  t,x,y=0,0,0
  flag=true
  for i=1:parse(Int,readline())
    t,x,y=parse.(Int,split(readline())).-(t,x,y)
    flag = flag && abs(x) + abs(y) <= t && iseven(x+y-t)
  end
  println(flag ? "Yes" : "No")
end
main()

解説

関数.()と同じように.演算子で配列の要素一般に及ぶ操作を行うことができます。
abs(x)xの絶対値を返します。

function main()
:
end
と囲んでいるのはこうしないとt,x,yfor内のローカルスコープを指すのか、
グローバルスコープを指すのかでズレが生じてしまうためです。

ただ、この仕様に関してはバージョンアップで変わる可能性が大いにあります。

解答例2 2020/09/04追記

let
  t,x,y=0,0,0
  flag=true
  for i=1:parse(Int,readline())
    t,x,y=parse.(Int,split(readline())).-(t,x,y)
    flag = flag && abs(x) + abs(y) <= t && iseven(x+y-t)
  end
  println(flag ? "Yes" : "No")
end

解説

関数にしなくても、letによってローカルなブロックを使用できるみたいです。
無駄な定義をしない分スマートですね。
@poheさんありがとうございます。

おわりに

なんとなくでもJuliaという言語の簡潔さが伝われば良いなと思います。
Julia、きれいですよね。

9
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6