“攒青豆”问题解析

126 阅读2分钟

当青训营遇上码上掘金:

题目简介

下面简要介绍一下题目“接青豆”的情况。

1.webp
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

我的思路

把列作为基本单元,那么每一列所能接的青豆取决于该列左边最高列的高度h1以及右边最高列的高度h2,那么在该列柱子加上青豆的高度便是 min(h1,h2) ,以上图中的第1列为例(列从0开始计数),其左边最高列为5,右边最高列为4,那么第一列能接的青豆数目便是 min(4,5)-0 = 4。针对特殊情况,比如最左边(或最右边)的列,可以认为其左边最高列(右边最高列)的高度为它们自身;对于数组中最大的数字,可以认为其左边最高列和右边最高列均是它自身。
那么现在的问题是如何求出每一列的左边最高列以及右边最高列。为了节省空间,建立一个长度为 n 的int型数组,用来存储 min(h1,h2)。共需遍历两遍数组,第一遍从左往右遍历,求出左边最高列h1,并记录进数组,如下图所示:

攒青豆.png.png 第二遍从右往左遍历,求出右边最高列h2,并把 min(h1,h2) 记录进数组。据此便可以求出接住的青豆的数目。

代码实现及介绍

实现所需功能的代码如下所示:

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	var a []int
	stdin := bufio.NewReader(os.Stdin)
	rs, err := stdin.ReadString('\n')
	if err != nil {
		fmt.Printf("An err happens:%v", err)
	}
	as := strings.Fields(rs)
	for _, c := range as {
		tmp, err := strconv.Atoi(c)
		if err != nil {
			continue
		}
		a = append(a, tmp)
	}
	n := len(a)
	ans := make([]int, n)
	mx, sum := 0, 0
	for i := 0; i < n; i += 1 {
		if a[i] > mx {
			mx = a[i]
		}
		ans[i] = mx - a[i]
	}
	mx = 0
	for i := n - 1; i >= 0; i -= 1 {
		if a[i] > mx {
			mx = a[i]
		}
		if mx-a[i] < ans[i] {
			ans[i] = mx - a[i]
		}
		sum += ans[i]
	}
	fmt.Print(sum)
}

为了使得读取更加高效,使用bufio进行输入数据的读取,先将输入的数据读取到string字符串中,遇到换行符停止读取,strings.Fields可以按照空格将字符串进行分割,返回的是分割出的字符串的切片,然后利用strconv 将字符串转化为整数,两个for循环分别对应着从左到右以及从右到左的遍历,mx记录着最高高度,在for循环中实时更新。将最终结果记录在sum中进行输出。