Go 语言解决 50 层台阶问题

问题描述

有50层台阶,一个人每步可以上一层或者两层,问一共有多少种上楼的方式。

问题分析

50层可以由 49 层跨一步,也可以由48层跨两步,49层则可以由48层跨一步或者47层跨两步,…只有第1层只能由第0层跨一步到达,由此可见,当n>1 时, 第 n 层可由 n-1 层跨一步或者 n-2 层跨两步到达。

code

package main

import (
	"fmt"
	"time"
)
func main() {
	fmt.Println("一段楼梯有n个台阶,每次只能上一层台阶或者两层台阶,输出有多少种上楼的可能。")
	fmt.Println("输入 n")
	var n int8
	fmt.Scanln(&n)
	fmt.Println("计算开始", time.Now())
	result := deal(n, 0)
	fmt.Println("计算结束", time.Now())
	fmt.Println(result)
}
func deal(m int8, s int) int {
	if m >= 2 {
		return deal(m-1, s) + deal(m-2, s)
	}
	s++
	return s
}

输出结果

一段楼梯有n个台阶,每次只能上一层台阶或者两层台阶,输出总所有多少种上楼的可能。
输入 n
50
计算开始 2020-09-14 15:30:20.253355305 +0800 CST m=+1.608072579
计算结束 2020-09-14 15:32:34.116474064 +0800 CST m=+135.471191265
20365011074

可以发现,虽然结果已经算出来了,但是通过输入 n 的变化可以发现,随着 n 的变大,计算的时间会增加的夸张,50层的计算已经需要 14 s的时间了。

优化分析

50 层可以由49层跨一步,也可以由48层跨两步,这里我们程序里会分为两种情况去计算,一种计算49层如何到达,一种计算48层怎么到达,然后49层就可以由48层跨一步或47层跨两步,这里又会计算48层怎么到达了,但是之前在计算50层如何到达的一种可能中就是48层如何到达,这里明细可以发现有重复的计算,所以我们可以使用一个字典去存已经计算过的数据,比如我们要计算48层如何到达,首先先看这个字典里有无48层的结果,如果有我们就直接用,不在去计算了,如果没有,则计算后插入字典。

优化code

package main

import (
	"fmt"
	"time"
)

var gmap = make(map[int8]int)

func main() {
	fmt.Println("一段楼梯有n个台阶,每次只能上一层台阶或者两层台阶,输出总所有多少种上楼的可能。")
	fmt.Println("输入 n")
	var n int8
	fmt.Scanln(&n)
	fmt.Println("计算开始", time.Now())
	//result := deal(n, 0)
	result := dealPlus(n, 0)
	fmt.Println("计算结束", time.Now())
	fmt.Println(result)
}
//优化前
func deal(m int8, s int) int {
	if m >= 2 {
		return deal(m-1, s) + deal(m-2, s)
	}
	s++
	return s
}
// 优化后
func dealPlus(m int8, s int) int {
	if m >= 2 {
		var onesetp int
		var twosetp int
		r1, ok1 := gmap[m-1]
		r2, ok2 := gmap[m-2]
		if ok1 {
			onesetp = r1
		} else {
			onesetp = dealPlus(m-1, s)
			gmap[m-1] = onesetp
		}
		if ok2 {
			twosetp = r2
			gmap[m-2] = twosetp
		} else {
			twosetp = dealPlus(m-2, s)
		}
		return onesetp + twosetp
	}
	s++
	return s
}

优化后输出

一段楼梯有n个台阶,每次只能上一层台阶或者两层台阶,输出总所有多少种上楼的可能。
输入 n
50
计算开始 2020-09-14 15:30:05.80893695 +0800 CST m=+3.284548177
计算结束 2020-09-14 15:30:05.809395276 +0800 CST m=+3.285006492
20365011074

可以看出优化后的计算时间耗时为毫秒级

问题扩展

一段楼梯有n个台阶,每次至少跨一层,至多跨m层,输出总共有多少种上楼的可能。

扩展解析

首先考虑 楼梯高度只有 m 层,每次至多跨 m 层,有多少种可能。当 m = 1 时,只有一种可能,当 m = 2 时,有两种可能,当 m = 3 时,有 4 种可能,m = 4 时,8种可能, m = 5 时,有 16 种可能…大概可以推测出 当 m = n 时, 有 2 的 (n-1 ) 次方种可能。
推测只是让我们找到思路去验证,并不代表推测的就一定是对的,没有验证的推测都是耍流氓。

验证: m 层 可由 m-1 ,m-2 ,… ,1 ,0层跨对应的1,2,…,m-1,m 层到达。入股以 S(m)作为到达 m 层的可能。 当 m >=1 时,则 S( m ) = S( m -1 ) + S( m-2 ) +…+S(1) +S(0) 注: S(0) = 1 。
熟悉又陌生的感觉,然后开始梦回高中

 因为    S(m) =  S( m-1 ) + S(m-2) +.....+ S(1) +S(0)
    		S ( m-1) = S(m-2) +S(m-3)+....+S(1)+S(0)
 所以    S(m) - S(m-1) = S( m-1 )
 即     	S(m) = 2 x S(m-1)
 又因为 S(1) = S(1-1) = S(0) = 0
 所以    S(m)= 2 的 m -1 次方 

所以可得,当楼层 n <= m 时,上楼的可能为 2 的 n-1 次方。
当楼层 n > m 时,和前面 50 层楼,每次 1-2 层台阶的问题几乎一致。

S(n) =  S( n-1) + S( n-2 ) +....+ S(n-m) 
func dealPlusPlus(m int, s int, max int) int {
	if m > max {
		var tmp int
		for i := 1; i <= max; i++ {
			r, ok := gmap[m-i]
			if ok {
				tmp += r
			} else {
				tmp += dealPlusPlus(m-i, s, max)
				gmap[m-i] = tmp
			}
		}
		return tmp
	}
	s = int(math.Pow(2, float64(m-1)))
	return s
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值