码上掘金之“接青豆”

116 阅读1分钟

当青训营遇上码上掘金,这是学习GO语言的第二天,为了赚取青豆,于是就找到“码上掘金”这个活动,一眼就看中了主题四,为什么呢?因为我觉得比较有意思,我就喜欢这种简单易懂的题目。 开始做题:一开始看到题目我就想着先做一个简单的从键盘输入给数组赋值,数组的长度也自己定义。我写的代码如下:

    package main
    inport(
            "fmt"
    )
    
    func main(){
        var n int
        fmt.Printfln("请输入数组的长度:")
        fmt.Scanf("%d",&n)
        height:=make([]int,n)
        //循环给数组赋值
        for i:=0;i<n;i++{
            var num int
            fmt.Scanf("%d",num)
            height[i]=num
        }
    }

但不知道为什么是错误的,每次输入都会出现和我想到结果不一样的结果,我也尝试去查找问题,但我根本不知道怎么做,于是我只能傻傻的定义题目给的数组,先把题目做出来再说!

简单讲一下我的思路吧

方法一:从底部往上遍历,按行求

比较笨的一种方法,但是也是最容易想到的;就是先求高度为1的水,在求高度为2的水,以此类推;大概思路就是:我们先假设高度为i的水柱,然后遍历柱子数组,如果在当前位置柱子高度小于i,并且两边有高度大于等于 i 的,说明这个地方一定有水,水就可以加1。可以用一个变量 temp 保存当前累积的水,初始化为 0。从左到右遍历墙的高度,遇到高度大于等于 i 的时候,开始更新 temp。更新原则是遇到高度小于 i 的就把 temp 加 1,遇到高度大于等于 i 的,就把 temp 加到最终的答案 ans 里,并且 temp 置零,然后继续循环。

方法二:依次遍历

有方法一联想到,某个地方有水,那么它的左边和右边一定要比它高才能储存水,所以我们遍历数组,每次遍历,都找到它左边比它高的值以及右边比它高的值,然后求出左边和右边比较小的值,如果这个值比当前位置的值还小,就舍去,否则就将他们的差加入结果。

方法三:动态规划

在方法二的基础上进行改进,因为每次遍历到一个值都需要查找它左右两边的值,会出现重复的情况,这个时候就可以用动态规划了,定义一个数组dp,从右往左遍历,dp[i]表示:下标i的右边的最大值;然后从左往右便利,用一个dp2来记录左边最大的值,然后就和方法二一样就ok了;代码如下:

package main

import (
	"fmt"
)

func max(a int, b int) int {
	if a > b {
		return a
	} else {
		return b
	}
}

func min(a int, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func main() {

	//height数组
	height := [...]int{5, 0, 2, 1, 4, 0, 1, 0, 3}

	//算法题:接雨水
	var size int = len(height)

	dp_right := make([]int, size, size)
	dp_left := make([]int, size, size)
	dp_right[size-1] = 0
	dp_left[0] = 0

	for i := size - 2; i >= 0; i-- {
		dp_right[i] = max(dp_right[i+1], height[i+1])
	}

	var sum int
	for i := 1; i < size; i++ {
		dp_left[i] = max(dp_left[i-1], height[i-1])
		var minnum int = min(dp_left[i], dp_right[i])
		if minnum < height[i] {
			continue
		} else {
			sum += (minnum - height[i])
		}
	}
	fmt.Println("最大能接的青豆为:", sum)
}

吐槽一点,go里面没有整形类型的max和min函数,并且我想用三目运算符都不行,虽然函数不难,但是懒啊,重新学一门语言,以前的一些习惯一时间难以更改啊