(待完善)Go ACM模式处理输入输出

本文由 简悦 SimpRead 转码, 原文地址 tbghg.top

这里是 TBH 的博客,用于技术分享,记录日常。

背景

面试时 ACM 模式较多,力扣以核心代码模式为主,特地训练下 ACM 模式处理输入输出

推荐阅读

举例

在此用洛谷中的一道题说明一下:P1886 滑动窗口 /【模板】单调队列

使用常规输入输出:

折叠代码块 GO 复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main

import (
"fmt"
"strings"
)

func main() {
var n, k int
fmt.Scanln(&n, &k)
list := make([]int, n)
for i := 0; i < n; i++ {
fmt.Scan(&list[i])
}
max, min := slidingWindow(list, k)

fmt.Println(strings.Trim(fmt.Sprint(min), "[]"))
fmt.Println(strings.Trim(fmt.Sprint(max), "[]"))
}

func slidingWindow(list []int, k int) (max, min []int) {
var maxWindow, minWindow []int

maxPush := func(i int) {
for len(maxWindow) > 0 && list[maxWindow[len(maxWindow)-1]] < list[i] {
maxWindow = maxWindow[:len(maxWindow)-1]
}
maxWindow = append(maxWindow, i)
}

minPush := func(i int) {
for len(minWindow) > 0 && list[minWindow[len(minWindow)-1]] > list[i] {
minWindow = minWindow[:len(minWindow)-1]
}
minWindow = append(minWindow, i)
}
for i := 0; i < k; i++ {
maxPush(i)
minPush(i)
}
max = append(max, list[maxWindow[0]])
min = append(min, list[minWindow[0]])
for i := k; i < len(list); i++ {
maxPush(i)
minPush(i)

for maxWindow[0] < i-k+1 {
maxWindow = maxWindow[1:]
}
for minWindow[0] < i-k+1 {
minWindow = minWindow[1:]
}
max = append(max, list[maxWindow[0]])
min = append(min, list[minWindow[0]])
}
return
}

结果就是有三个超时,其他通过,可自行尝试

分析下原因,每次fmt.Scan时都会进行一次系统调用,如果 N 较大,那时间开销相对就比较大,我们可以使用bufio库将数据直接读取到缓存中,整体只需要一次系统调用,时间开销要小很多

相应的,输出时如果我们选择 for 循环每次 print 一个数据,那时间开销也会很长,但将数据通过strings.Trim(fmt.Sprint(min), "[]")处理好之后再输出,总共两次fmt.Println系统开销要小很多。同时我们也可以把结果放到缓冲区中,再通过Flush将数据写出

我们先来看处理输入:

折叠代码块 GO 复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import (
"bufio"
"fmt"
"os"
"strings"
)

func main() {
var n, k int
fmt.Scanln(&n, &k)
list := make([]int, n)



in := bufio.NewReader(os.Stdin)



for i := 0; i < n; i++ {
fmt.Fscan(in, &list[i])
}

max, min := slidingWindow(list, k)
fmt.Println(strings.Trim(fmt.Sprint(min), "[]"))
fmt.Println(strings.Trim(fmt.Sprint(max), "[]"))
return
}

修改后,所有数据点可通过

我们再来看下输出,这时我们用了两次fmt.Println,也就是会进行两次系统调用,那我们如果将结果写入输出的缓存中,最后os.Flush是不是可以对时间进一步优化呢,原则上是这样的:

折叠代码块 GO 复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
var n, k int
fmt.Scanln(&n, &k)
list := make([]int, n)


in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)

for i := 0; i < n; i++ {
fmt.Fscan(in, &list[i])
}

max, min := slidingWindow(list, k)



fmt.Fprint(out, fmt.Sprint(strings.Trim(fmt.Sprint(min), "[]")+"\n"+strings.Trim(fmt.Sprint(max), "[]")))

out.Flush()
return
}

最后的运行结果是有一个点报 MLE 了(按照第二种写法,那个点是 100.16MB)

这个也比较好理解,对于数据量比较变态的情况,及时将数据输出,可以预防 MLE,所有结果合起来再输出,就有可能会超出规定内存

当然,输入也有可能会超出默认缓存大小,这个时候需要根据实际情况进行调整,例如 https://www.acwing.com/blog/content/28740/ 中提到的:

特殊场合 当一次输入超大时:3302. 表达式求值

将输入缓存设置为20000 * 1024

折叠代码块 GO 复制代码

1
2
3
4
in = bufio.NewScanner(os.Stdin)
bs = make([]byte, 20000 * 1024)

in.Buffer(bs, len(bs))

(待完善)Go ACM模式处理输入输出

https://blog.jiejaitt.top/posts/7c20dcb8.html

作者

JIeJaitt

发布于

2023-08-01

更新于

2023-08-01

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×