6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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)
			}
		})
	}
}

まとめ

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

6
3
0

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
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?