当青训营遇上码上掘金,这是学习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函数,并且我想用三目运算符都不行,虽然函数不难,但是懒啊,重新学一门语言,以前的一些习惯一时间难以更改啊