AtCoder過去問精選10問をGo + テスト付きで解いてみた

Goの標準テスト練習として、テストファーストで [ AtCoder に登録したら解くべき精選 10 問 ]((https://qiita.com/drken/items/fd4e5e3630d0f5859067)
)を解いてみました。

## 第 1 問: [ABC 086 A - Product](https://beta.atcoder.jp/contests/abc086/tasks/abc086_a) (100 点)

標準入力出力と関数を分け、出題の入出力例を Table Driven Tests に書き換えています。

```go: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"
}
```

```go: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](https://beta.atcoder.jp/contests/abc081/tasks/abc081_a) (100 点)

A問題くらいだとテストコードの方が長くなりますね。

```go: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
}
```

```go: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](https://beta.atcoder.jp/contests/abc081/tasks/abc081_b) (200 点)

テストファーストだと、テストケース構造体を書く時に標準入力の格納方法を考える、という思考順序になりました。

```go: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))
}
```
```go: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](https://beta.atcoder.jp/contests/abc087/tasks/abc087_b) (200 点) 

```go: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))
}
```
```go: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](https://beta.atcoder.jp/contests/abc083/tasks/abc083_b) (200 点) 

```go: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
}
```

```go: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](https://beta.atcoder.jp/contests/abc088/tasks/abc088_b) (200点)

```go: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))
}
```

```go: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](https://beta.atcoder.jp/contests/abc085/tasks/abc085_b) (200点)

```go: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))
}
```

```go: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](https://beta.atcoder.jp/contests/abc085/tasks/abc085_c) (300点)

この 085C の様にケースに対する回答が複数パターンあるものの扱いはちょっと考えています。

```go: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))
}
```

```go: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](https://beta.atcoder.jp/contests/abc049/tasks/arc065_a) (300点)

```go: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))
}
```
```go: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](https://beta.atcoder.jp/contests/abc086/tasks/arc089_a) (300点)

```go: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))
}
```
```go: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)
			}
		})
	}
}
```

## まとめ
解くスピードを競う点に関して一切メリットが無いですが、標準テスト書きの素振りにおすすめです。