Goの標準テスト練習として、テストファーストで AtCoder に登録したら解くべき精選 10 問 を解いてみました。
第 1 問: ABC 086 A - Product (100 点)
標準入力出力と関数を分け、出題の入出力例を Table Driven Tests に書き換えています。
main.go
package main
import "fmt"
func main() {
var a, b int
fmt.Scan(&a, &b)
fmt.Println(evenodd(a * b))
}
func evenodd(i int) string {
if i%2 == 0 {
return "Even"
}
return "Odd"
}
main_test.go
package main
import "testing"
func TestEvenodd(t *testing.T) {
cases := map[string]struct {
a, b int
want string
}{
"1": {a: 3, b: 4, want: "Even"},
"2": {a: 1, b: 21, want: "Odd"},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := evenodd(tc.a * tc.b); got != tc.want {
t.Fatalf("want = %v, got = %v", tc.want, got)
}
})
}
}
第 2 問: ABC 081 A - Placing Marbles (100 点)
A問題くらいだとテストコードの方が長くなりますね。
main.go
package main
import "fmt"
func main() {
var input string
fmt.Scan(&input)
fmt.Println(one(input))
}
func one(input string) int {
var i int
for n := range input {
if input[n] == '1' {
i++
}
}
return i
}
main_test.go
package main
import "testing"
func TestOne(t *testing.T) {
cases := map[string]struct {
in string
want int
}{
"1": {in: "101", want: 2},
"2": {in: "000", want: 0},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := one(tc.in); got != tc.want {
t.Fatalf("want = %v, got = %v", tc.want, got)
}
})
}
}
第 3 問: ABC 081 B - Shift Only (200 点)
テストファーストだと、テストケース構造体を書く時に標準入力の格納方法を考える、という思考順序になりました。
main.go
package main
import "fmt"
func change(n int, list []int) int {
var c int
var oddFlag bool
for {
for i, num := range list {
if num%2 != 0 {
oddFlag = true
break
}
list[i] = num / 2
}
if oddFlag == true {
break
}
c++
}
return c
}
func main() {
var n int
fmt.Scan(&n)
a := make([]int, n)
for i := range a {
fmt.Scan(&a[i])
}
fmt.Println(change(n, a))
}
main_test.go
package main
import "testing"
func TestChange(t *testing.T) {
cases := map[string]struct {
n int
list []int
want int
}{
"1": {n: 3, list: []int{8, 12, 40}, want: 2},
"2": {n: 4, list: []int{5, 6, 8, 10}, want: 0},
"3": {n: 4, list: []int{382253568, 723152896, 37802240, 379425024, 404894720, 471526144}, want: 8},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := change(tc.n, tc.list); got != tc.want {
t.Fatalf("want %v, got %v", tc.want, got)
}
})
}
}
第 4 問: ABC 087 B - Coins (200 点)
main.go
package main
import "fmt"
func coins(a, b, c, x int) int {
var cnt int
for i := 0; i <= a; i++ {
for j := 0; j <= b; j++ {
for k := 0; k <= c; k++ {
if 500*i+100*j+50*k == x {
cnt++
}
}
}
}
return cnt
}
func main() {
var a, b, c, x int
fmt.Scan(&a, &b, &c, &x)
fmt.Println(coins(a, b, c, x))
}
main_test.go
package main
import "testing"
func TestPattern(t *testing.T) {
cases := map[string]struct {
a, b, c, x int
want int
}{
"1": {a: 2, b: 2, c: 2, x: 100, want: 2},
"2": {a: 5, b: 1, c: 0, x: 150, want: 0},
"3": {a: 30, b: 40, c: 50, x: 6000, want: 213},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := coins(tc.a, tc.b, tc.c, tc.x); got != tc.want {
t.Fatalf("want is %v, got is %v", tc.want, got)
}
})
}
}
第 5 問: ABC 083 B - Some Sums (200 点)
main.go
package main
import "fmt"
func main() {
var n, a, b int
fmt.Scan(&n, &a, &b)
fmt.Println(sum(n, a, b))
}
func sum(n, a, b int) int {
var t int
for i := 1; i <= n; i++ {
s := sumDigits(i)
if s >= a && s <= b {
t += i
}
}
return t
}
func sumDigits(n int) int {
var s int
for n > 0 {
s += n % 10
n /= 10
}
return s
}
main_test.go
package main
import "testing"
func TestSum(t *testing.T) {
cases := map[string]struct {
n, a, b, want int
}{
"1": {n: 20, a: 2, b: 5, want: 84},
"2": {n: 10, a: 1, b: 2, want: 13},
"3": {n: 100, a: 4, b: 16, want: 4554},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := sum(tc.n, tc.a, tc.b); got != tc.want {
t.Fatalf("want is %v, got is %v", tc.want, got)
}
})
}
}
第 6 問: ABC 088 B - Card Game for Two (200点)
main.go
package main
import (
"fmt"
"sort"
)
func game(n int, a []int) int {
var diff int
sort.Sort(sort.Reverse(sort.IntSlice(a)))
for i, num := range a {
if i%2 == 0 {
diff += num
} else {
diff -= num
}
}
return diff
}
func main() {
var n int
fmt.Scan(&n)
a := make([]int, n)
for i := range a {
fmt.Scan(&a[i])
}
fmt.Println(game(n, a))
}
main_test.go
package main
import "testing"
func TestGame(t *testing.T) {
cases := map[string]struct {
n int
a []int
want int
}{
"1": {n: 2, a: []int{3, 1}, want: 2},
"2": {n: 3, a: []int{2, 7, 4}, want: 5},
"3": {n: 4, a: []int{20, 18, 2, 18}, want: 18},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := game(tc.n, tc.a); got != tc.want {
t.Fatalf("want is %v, got is %v", tc.want, got)
}
})
}
}
第 7 問: ABC 085 B - Kagami Mochi (200点)
main.go
package main
import "fmt"
func bucket(d []int) int {
b := map[int]int{}
for i := range d {
b[d[i]]++
}
return len(b)
}
func main() {
var n int
fmt.Scan(&n)
d := make([]int, n)
for i := range d {
fmt.Scan(&d[i])
}
fmt.Println(bucket(d))
}
main_test.go
package main
import "testing"
func TestMochi(t *testing.T) {
cases := map[string]struct {
n int
d []int
want int
}{
"1": {n: 4, d: []int{10, 8, 8, 6}, want: 3},
"2": {n: 3, d: []int{15, 15, 15}, want: 1},
"3": {n: 7, d: []int{50, 30, 50, 100, 50, 80, 30}, want: 4},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := bucket(tc.d); got != tc.want {
t.Fatalf("want is %v, got is %v", tc.want, got)
}
})
}
}
第 8 問: ABC 085 C - Otoshidama (300点)
この 085C の様にケースに対する回答が複数パターンあるものの扱いはちょっと考えています。
main.go
package main
import (
"fmt"
)
func dama(n, y int) (int, int, int) {
d := make([]int, n+1)
a, b, c := -1, -1, -1
for i := range d {
for j := range d {
k := n - i - j
if k < 0 {
continue
}
if y == 10000*i+5000*j+1000*k {
a = i
b = j
c = k
return a, b, c
}
}
}
return a, b, c
}
func main() {
var n, y int
fmt.Scan(&n, &y)
fmt.Println(dama(n, y))
}
main_test.go
package main
import (
"reflect"
"testing"
)
func TestDama(t *testing.T) {
cases := map[string]struct {
n, y int
want []int
}{
"1": {n: 9, y: 45000, want: []int{0, 9, 0}},
"2": {n: 20, y: 196000, want: []int{-1, -1, -1}},
"3": {n: 1000, y: 1234000, want: []int{2, 54, 944}},
"4": {n: 2000, y: 20000000, want: []int{2000, 0, 0}},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
a, b, c := dama(tc.n, tc.y)
got := []int{a, b, c}
if !reflect.DeepEqual(got, tc.want) {
t.Fatalf("want is %v , got is %v", tc.want, got)
}
})
}
}
第 9 問: ABC 049 C - Daydream (300点)
main.go
package main
import (
"fmt"
)
func dream(s string) string {
dp := make([]int, len(s)+1)
dp[0] = 1
for i := range s {
if dp[i] == 0 {
continue
}
if i+5 <= len(s) && s[i:i+5] == "dream" {
dp[i+5] = 1
}
if i+7 <= len(s) && s[i:i+7] == "dreamer" {
dp[i+7] = 1
}
if i+5 <= len(s) && s[i:i+5] == "erase" {
dp[i+5] = 1
}
if i+6 <= len(s) && s[i:i+6] == "eraser" {
dp[i+6] = 1
}
}
if dp[len(s)] == 1 {
return "YES"
}
return "NO"
}
func main() {
var s string
fmt.Scan(&s)
fmt.Println(dream(s))
}
main_test.go
package main
import "testing"
func TestDream(t *testing.T) {
cases := map[string]struct {
s, want string
}{
"1": {s: "erasedream", want: "YES"},
"2": {s: "dreameraser", want: "YES"},
"3": {s: "dreamerer", want: "NO"},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := dream(tc.s); got != tc.want {
t.Fatalf("want is %v, got is %v", tc.want, got)
}
})
}
}
第10問: ABC 086 C - Traveling (300点)
main.go
package main
import (
"fmt"
"math"
)
func abs(n int) int {
return int(math.Abs(float64(n)))
}
func travel(n int, t, x, y []int) string {
post, posx, posy := 0, 0, 0
for i := range t {
difft := abs(t[i] - post)
diffx := abs(x[i] - posx)
diffy := abs(y[i] - posy)
if difft < diffx+diffy {
return "No"
}
if difft%2 != (diffx+diffy)%2 {
return "No"
}
post = t[i]
posx = x[i]
posy = y[i]
}
return "Yes"
}
func main() {
var n int
fmt.Scan(&n)
time, x, y := make([]int, n), make([]int, n), make([]int, n)
for i := range time {
fmt.Scan(&time[i])
fmt.Scan(&x[i])
fmt.Scan(&y[i])
}
fmt.Println(travel(n, time, x, y))
}
main_test.go
package main
import "testing"
func TestTravel(t *testing.T) {
cases := map[string]struct {
n int
time, x, y []int
want string
}{
"1": {n: 2, time: []int{3, 6}, x: []int{1, 1}, y: []int{2, 1}, want: "Yes"},
"2": {n: 1, time: []int{2}, x: []int{100}, y: []int{100}, want: "No"},
"3": {n: 2, time: []int{5, 100}, x: []int{1, 1}, y: []int{1, 1}, want: "No"},
}
for n, tc := range cases {
tc := tc
t.Run(n, func(t *testing.T) {
if got := travel(tc.n, tc.time, tc.x, tc.y); got != tc.want {
t.Fatalf("want is %v, got is %v", tc.want, got)
}
})
}
}
まとめ
解くスピードを競う点に関して一切メリットが無いですが、標準テスト書きの素振りにおすすめです。