技巧
输入输出
笔试可能会遇到牛客网 ACM 模式的题目,所以要熟悉一下各种语言的输入输出
Golang
基本格式
基本写法:用一个 for 循环包着,用 Scan 函数接收用空白分隔符分开来的参数。
package main
import "fmt"
func main() {
   var a, b int
   for { 
      // 循环获取输入
      if _, err := fmt.Scan(&a, &b); err == nil {
         fmt.Println(a + b)
      } else {
         break
      }
   }
}
另一种写法,判断 err 是否是 io.EOF 类型
package main 
import (  
    "fmt"
    "io"
)
func main() {
    var a, b int
    for { 
        if _, err := fmt.Scan(&a,&b); err != io.EOF {
            fmt.Println(a + b)
        } else {
            break
        }
    }
}
包含输入个数
如果题目提供了输入参数的个数 t,那就在循环外提前接收:
package main
import "fmt"
func main() {
    var t, a, b int
    fmt.Scan(&t) // 返回的参数都省略掉了
    for t > 0 {
        fmt.Scan(&a, &b)
        fmt.Println(a + b)
        t--
    }
}
包含退出条件
如果题目提供了退出的条件,也不用接受 io.EOF 了,在循环内判断退出条件即可:
package main
import "fmt"
func main() {
    var a, b int
    for {
        fmt.Scan(&a, &b)
        if a == 0 && b == 0 {
            // 退出条件就是两者均为0
            break
        }
        fmt.Println(a + b)
    }
}
不固定参数个数
如果题目的一行输入个数是不固定的,并且每行第一个数代表了参数个数:
package main
import "fmt"
func main() {
    var n, num int
    for {
        fmt.Scan(&n) // 先取每行第一个数,确定个数
        if n == 0 {
            // 退出条件,总会给你的
            break
        }
        sum := 0
        for n > 0 {
            // 再套个循环,写真正的业务逻辑
            fmt.Scan(&num)
            sum += num
            n--
        }
        fmt.Println(sum)
    }
}
这种情况还可以用 bufio 来做:
package main
import (  
    "fmt"
    "os"
    "bufio"
    "strings"
    "strconv"
)
func main() {
    input := bufio.NewScanner(os.Stdin) // 创建并返回一个从os.Stdin读取数据的Scanner
    for input.Scan(){ 
        // Scan方法获取当前位置的token(该token可以通过Bytes或Text方法获得),
        // 并让Scanner的扫描位置移动到下一个token。
        // 当扫描因为抵达输入流结尾或者遇到错误而停止时,本方法会返回false
        nums := strings.Split(input.Text(), " ") // 分割字符串
        if nums[0] == "0" { // 判断是否结束
            break
        }
        res := 0
        for i := 1; i < len(nums); i++ {
            num, _ := strconv.Atoi(nums[i]) // 字符串转数字
            res += num
        }
        fmt.Println(res)
    }
}
这种方法比较麻烦。。。在接收字符串类型的情况时会比较好用
有时候不会给你退出条件,而是在开头提供了行数:
package main
import (
    "fmt"
)
func main() {
    var t, n, num int
    fmt.Scan(&t)
    for ; t > 0; t-- {
        fmt.Scan(&n)
        res := 0
        for ; n > 0; n-- {
            fmt.Scan(&num)
            res += num
        }
        fmt.Println(res)
    }
}
不固定参数个数,且不提供个数
只能用 bufio 了。。。
package main
import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)
func main() {
	input := bufio.NewScanner(os.Stdin)
	for input.Scan() {
		nums := strings.Split(input.Text(), " ")
		if len(nums) == 0 {
			break
		}
		res := 0
		for _, num := range nums {
			tmp, _ := strconv.Atoi(num)
			res += tmp
		}
		fmt.Println(res)
	}
}
希望这种题远离我
Python
nums = list(map(int, input().split()))
数组
双指针
计算过程仅与两端点相关的称为双指针。
这种方法通过一个快指针和慢指针在一个 for 循环下完成两个 for 循环的工作。
- 快慢指针:都从头开始,遍历条件不同,所以速度不同
 - 头尾指针:一个从头,一个从尾;二分法就是一种头尾指针;也叫对撞指针
 
题目
头尾指针:[offer 57](array\剑指 Offer 57. 和为 s 的两个数字.md)
快慢指针:
滑动窗口
计算过程与两端点表示的区间相关的称为滑动窗口。
滑动窗口本身并不是解决问题的一种方法(或者说算法),它其实就是问题本身。
滑动窗口一定是同向移动的。