AtCoder に登録したら解くべき精選過去問 10 問を V言語 で改めて解いてみた
どうも。
私は4年前(2023/3/27現在)にAtCoder に登録したら解くべき精選過去問 10 問を V言語 で解いてみたという記事を投稿しました。
これを書いたころはまだまだV言語は未熟な言語でしたが、現在は段々安定してきておりほぼほぼ実用レベルに達していきつつあります。
ところでAtCoderは2023年の言語アップデートを始めたそうで、その中にはV言語があります。
そして以下のコンテストからなんと現在V言語を試すことができます。AtCoderにV言語があるが?????????そこで、もう一回この問題たちを解いてみようと思います。前の記事は現在のV言語に沿わないものもありますので、その点を踏まえていきます。
V言語ホームページ https://vlang.io/
V言語GitHub https://github.com/vlang/v
ABS(AtCoder) https://atcoder.jp/contests/abs
入力
前の記事を書いたころはC言語とのFFIを用いて書いていました。勿論今でもそれをすることはできるのですが、今回はそれを避けようと思います。現在使える入力方法は2つあります。
os.get_line()
1行1行標準入力からstring
を入力していってそれを地道にint
に変換する方法です。
どこぞのJavaScriptみたいな感じです。
import os
a := os.get_line().int() // 1行を1つのintにする
bc := os.get_line().split(' ').map(it.int()) // 1行の2つ以上の数字をintにする
ss := os.get_lines() // 複数行を入力し文字の配列にする
ただし、入力フォーマット(スペースや改行の位置)を問題ごとに確認する手間が発生します。
そこで、今回は次の方法を用います。
proconio ライブラリを使う
手前味噌ですが、拙作のproconio
ライブラリを使う方法です。詳しい解説は以下にあります。
Language Testsで使えるようになったため、今回はこちらの手法を用いて解説していきます。
解答
PracticeA - はじめてのあっとこーだー(Welcome to AtCoder)
0問目import lemoncmd.proconio as pio
struct Input {
a int
b int
c int
s string
}
i := pio.input[Input]()
println('${i.a + i.b + i.c} ${i.s}')
入出力を問われている問題です。
V言語のスクリプト記法を用いています。
これは、コードをベタに書くことでさもmain関数の中に書かれているかのように扱ってくれる機能です。
せっかち競プロerには嬉しい機能ですね。
また、文字列リテラルの中で${式}
と書くことで、式を文字列化して文字列の中に入れてくれます。
これは出力には便利ですね。
ABC086A - Product
1問目import lemoncmd.proconio as pio
struct Input {
a int
b int
}
i := pio.input[Input]()
println(if i.a * i.b % 2 == 0 { 'Even' } else { 'Odd' })
簡単な数値演算です。
V言語のif文はRustやKotlinみたいな最近の言語らしく、三項演算子のように値を返すことができます。
ABC081A - Placing Marbles
2問目import lemoncmd.proconio as pio
import arrays
s := pio.input[string]().split('').map(it.int())
println(arrays.sum(s)!)
文字列処理...のはずですが、今回は少しズルをしています。
string
のsplit
メソッドによって文字を1文字ずつ分割をしてそれを数値化し、arrays.sum
関数でその総和を取っています。
なお、!
はエラー処理をする演算子で、関数の実行時にエラーが起こった際にpanicします。
ABC081B - Shift only
3問目import lemoncmd.proconio as pio
import math
struct Input {
n int
a []int [n]
}
mut a := pio.input[Input]().a
mut min := 1000
for mut ai in a {
mut x := 0
for ai % 2 == 0 {
x++
ai >>= 1
}
min = math.min(min, x)
}
println(min)
2で割れる回数を求めてその最小値を出します。
V言語では変数を何回でも代入可能にするためには変数宣言の前にmut
キーワードを付ける必要があります。
また、V言語にはfor文が4種類あり、それぞれ無限ループ、while、イテレーション、Cスタイルです。
今回は外側のfor文は要素が代入可能なイテレーション、内側のfor文は他の言語で言えばwhile文のような条件指定を行っています。
少ない値がどちらかを判定するのにmath.min
関数を使っています。
この関数はジェネリクスを使っているため、どの比較可能な型でも使えます。
ABC087B - Coins
4問目import lemoncmd.proconio as pio
struct Input {
a int
b int
c int
x int
}
i := pio.input[Input]()
mut count := 0
for aa in 0..i.a+1 {
for bb in 0..i.b+1 {
for cc in 0..i.c+1 {
if 500*aa + 100*bb + 50*cc == i.x {
count++
}
}
}
}
println(count)
有り得る全ての枚数について全探索します。
制約と計算量からして愚直解で通ります。
ABC083B - Some Sums
5問目import lemoncmd.proconio as pio
import arrays
struct Input {
n int
a int
b int
}
i := pio.input[Input]()
mut count := 0
for j in 0..i.n+1 {
sum := arrays.sum(j.str().split('').map(it.int()))!
if i.a <= sum && sum <= i.b {
count += j
}
}
println(count)
1からnの数について、全ての桁を足します。
今回は数値を文字列に変換し、Placing Marblesと同じ方法で各桁を足す方法を用いました。
ABC088B - Card Game for Two
6問目import lemoncmd.proconio as pio
struct Input {
n int
a []int [n]
}
mut a := pio.input[Input]().a
a.sort(a > b)
mut ans := 0
mut t := 1
for aa in a {
ans += t * aa
t = -t
}
println(ans)
降順にソートして足し引きをします。
ソートはarray
のsort
メソッドを利用します。
sort
は特殊なメソッドです。a
とb
という特殊な変数が渡され、括弧内に式を書けばそれがソートの条件文として機能します。
ABC085B - Kagami Mochi
7問目import lemoncmd.proconio as pio
struct Input {
n int
d []int [n]
}
mut d := pio.input[Input]().d
d.sort()
mut count := 0
mut before := -1
for dd in d {
if before != dd {
count++
}
before = dd
}
println(count)
昇順にクイックソートして被りを抜いて数えます。
sort
関数の条件文を省略すると勝手に昇順になります。
ABC085C - Otoshidama
8問目import lemoncmd.proconio as pio
struct Input {
n int
y int
}
i := pio.input[Input]()
n := i.n
y := i.y
mut a := y / 10000
mut b := (y - a * 10000) / 5000
mut c := (y - a * 10000 - b * 5000) / 1000
if a + b + c > n || y / 1000 < n {
println('-1 -1 -1')
return
}
for n > a + b + c {
if n - a - b - c >= 9 && a > 0 {
a--
c += 10
} else if n - a - b - c >= 4 && b > 0 {
b--
c += 5
} else if a > 0 {
a--
b += 2
} else {
println('-1 -1 -1')
return
}
}
println('${a} ${b} ${c}')
前回のようにお札の枚数で全探索してもよかったのですが、今回は真面目に貪欲法でお札の枚数を求めてからお札を両替していきます。
スクリプト構文で途中でプログラムを終了するには、main関数で書くときと同様にreturn文を書きます。
ABC049C - 白昼夢 / Daydream
9問目import lemoncmd.proconio as pio
println(if pio.input[string]().replace('eraser', '').replace('erase', '').replace('dreamer', '').replace('dream', '') == '' {
'YES'
} else {
'NO'
})
いつもの嘘解法です。
ABC086C - Traveling
10問目import lemoncmd.proconio as pio
import math
pub struct Time {
t int
x int
y int
}
struct Input {
n int
ts []Time [n]
}
ts := pio.input[Input]().ts
mut before := Time{0, 0, 0}
for t in ts {
dt := t.t - before.t
dxdy := math.abs(t.x - before.x) + math.abs(t.y - before.y)
if dxdy > dt || (dt - dxdy) % 2 == 1 {
println('No')
return
}
before = t
}
println('Yes')
歩数が足りるかどうかと、往復で時間をつぶしてたどり着けるか(偶奇でわかる)を判定します。
なお、ライブラリの都合でstructを入れ子にする場合はpub
を内側のstruct宣言の手前に書いてください。
あとがき
このままV言語がAtCoderに実装されたらうれしいですね。
簡単な構文で書けてしかも実行速度もC言語並みに速いV言語、競プロにいかがでしょうか。
Discussion