Goで競技プログラミング 1

はじめに

Go言語を知るきっかけとするために、競技プログラミングをGo言語で記載しています。
今回は、Goで書くときに使用しているコードのテンプレートをGistに置いているので紹介します。

環境

Go 1.6

補足

paizaではGoのバージョンが少し高いので、sort.StableSort が使用できるのですが、
Atcoderでは1.6のためsort.StableSort は使用できなかったです。
なので、コメントアウトしています。

コード

package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
// "regexp"
)
type Pair struct {
p1, p2 interface{}
}
const (
initialBufSize = 1e4
maxBufSize = 1e8
)
var (
scanner = bufio.NewScanner(os.Stdin)
writer = bufio.NewWriter(os.Stdout)
)
func main() {
buf := make([]byte, initialBufSize)
scanner.Buffer(buf, maxBufSize)
scanner.Split(bufio.ScanWords)
// TODO:処理
fmt.Println(readFloat())
}
/*==========================================
* Library
*==========================================*/
func write(s string) {
writer.WriteString(s)
}
func print() {
writer.Flush()
}
// scanner.Split(bufio.ScanWords) をコメントアウトしないと使用不可
func readLine() (s string) {
if scanner.Scan() {
s = scanner.Text()
}
return s
}
func readInt() (read int) {
scanner.Scan()
read, err := strconv.Atoi(scanner.Text())
if err != nil {
panic(err)
}
return
}
func readFloat() (read float64) {
scanner.Scan()
read, err := strconv.ParseFloat(scanner.Text(), 64)
if err != nil {
panic(err)
}
return
}
func readRunes() (read []rune) {
scanner.Scan()
for _, v := range scanner.Text() {
read = append(read, v)
}
return
}
func readString() (read string) {
scanner.Scan()
read = scanner.Text()
return
}
func readStrings() (read []string) {
scanner.Scan()
for _, v := range scanner.Text() {
read = append(read, string(v))
}
return
}
func s2i(s string) int {
var intVal, e = strconv.Atoi(s)
if e != nil {
panic(e)
}
return intVal
}
func i2s(i int) string {
var strVal = strconv.Itoa(i)
return strVal
}
func s2f(s string) float64 {
var floatVal, e = strconv.ParseFloat(s, 64)
if e != nil {
panic(e)
}
return floatVal
}
func sum(i []int) int {
sum := 0
for _, val := range i {
sum += val
}
return sum
}
func split(s string) []string {
return strings.Fields(s)
}
func strAry2intAry(strs []string) []int {
var ret = make([]int, 0, len(strs))
for _, str := range strs {
var intVal, e = strconv.Atoi(string(str))
if e != nil {
panic(e)
}
ret = append(ret, intVal)
}
return ret
}
func intAry2strAry(nums []int) []string {
var ret = make([]string, 0, len(nums))
for _, num := range nums {
var strVal = strconv.Itoa(num)
ret = append(ret, strVal)
}
return ret
}
func ary2str(strs []string) string {
return strings.Join(strs, " ")
}
func reverse(res []int) []int {
for i, j := 0, len(res)-1; i < j; i, j = i+1, j-1 {
res[i], res[j] = res[j], res[i]
}
return res
}
func initalize(res []int, init int) {
if len(res) == 0 {
return
}
res[0] = init
for i := 0; i < len(res); i++ {
copy(res[i:], res[:i])
}
}
//
// func regexpExample() {
// // Your code here!
// var str = "13:20"
// r := regexp.MustCompile(`(\d+):(\d+)`)
// fmt.Println(r.FindStringSubmatch(str))
// }
// type Country struct {
// gold int
// silver int
// blonze int
// }
// // 複数ソートのサンプル
// func stableSortExample() []Country{
// var country = []Country {
// {gold:1, silver:2, blonze:3},
// {gold:1, silver:2, blonze:3},
// {gold:1, silver:3, blonze:2},
// {gold:1, silver:3, blonze:3},
// }
// sort.SliceStable(country, func(i, j int) bool { return country[i].blonze > country[j].blonze })
// sort.SliceStable(country, func(i, j int) bool { return country[i].silver > country[j].silver })
// sort.SliceStable(country, func(i, j int) bool { return country[i].gold > country[j].gold })
// return country
// }
view raw competitive.go hosted with ❤ by GitHub
https://gist.github.com/katsuobushiFPGA/8a5ceac12bb7db5d1c38cb267a5dffc2

コードについての補足

readLine

標準入力から1行読み取りをする。
Revisions を見るとバレてしまうのですが、初めの方は bufio.NewScanner を使用していました。
bufio.NewScanner を使用していたときは、1行を読み取るサイズがデフォルトのままだったので、
下記に記載の通り、 MaxScanTokenSize までしか読み取れていませんでした。
それに気づかずに、 Atcoder Beginner Contest 148 で試行錯誤してた際にWAしてました。。

bufio.NewScanner を使用するときには、 Buffer を呼んでバッファサイズを変更すれば良さそうです。
https://golang.org/pkg/bufio/

Gistのコードでは、下記のような感じで グローバルに readerwriter に分けて宣言し、最大バッファのサイズを指定してます。

const (
    initialBufSize = 10000
    maxBufSize = 1000000
)
var reader = bufio.NewReaderSize(os.Stdin, maxBufSize)
var writer = bufio.NewWriter(os.Stdout)

2020/01/13 追記 上記の書き方だと、1行のサイズがおもすぎて配列に展開する際にメモリ消費量がやばかったので、やめました。
Atcoder Beginner Contest 151 にて TLEしたので(本当は map 使いまくったせいなのですが)、ついでに直しました。
今は、readInt, readString, readRunes みたいなヘルパー関数用意して、word ごとに読み取ってます。
メモリ効率良さげなので

s2i s2f i2s

strconv.Atoi を書くのが面倒なので短い名前の関数にラップしているだけです。
s2i: string -> int
s2f: string -> float64
i2s: int -> string

strAry2intAry intAry2strAry

こちらも同じ系統です。
よく使う用途は、

r = readLine()
is := strAry2intAry(split(r))

のような感じで、1行読み込みのときに、全部 string で返ってくるのを
[]stringに変換してこれを []int に変換します。

ary2str

期待の出力する際に、 []string -> string するときがあるのでそのため用

reverse sum

あまり使わないけどあったらたまに使う程度
名前の通りの動作をしてくれます。

stableSortExample

安定ソートがほしいときに参考にして使います。
構造体の配列をソートする際に、第1ソート, 第2ソートみたいな感じでソートできるので良いです。

おわりに

paiza の方は長らく放置していたので 2019年11月頃からまたはじめました。 (Bランク完答を目指している途中です)
Atcoderの方は登録したままでコンテストに一切出ていなかったので、 ABC 148から出ております。
2020年内に Atcoderの方はレート1000は行きたいです。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。