一道关于老鼠繁衍的算法题

题目源头

这道题目是前些日子,面试阿里的时候,给的一道在线测评算法题

题目描述

大海中央的一座孤岛,上面本来没有老鼠。有一艘路过的船只在附近沉没了,船上 x 对 新出生的老鼠,顺着船只残骸漂流到小岛上。
1. 每两个月一次生产,每次出生 2对 老鼠 
2. 老鼠的寿命是 5个月,也就是说一辈子可以生产 2次
3. 这个岛上,每个月因为神秘原因,假如存在 3个月大的老鼠,会死去一对
 
问 n个月后,岛上有多少只老鼠 (假设存活率 100% )

解题思路

1
  • 如果只看第一个条件,颇有一点斐波那契数列的样子。但是这个题目,老鼠不是单纯的生,还有死、意外死,还有生的限制
2
  • 进一步,可以看到,这个题目似乎老鼠是有几个状态的:新生的老鼠,意外死亡,正常死亡的老鼠等
  • 最开始在想,是不是可以存在几个状态转移方程,譬如fn=nx,n个月时该状态下的老鼠数量,多个状态转移与岛上的老鼠有什么关系,去寻找这种关系
  • 但是,想了一会儿,发现这种思路,偏复杂,比如意外死亡是需要考虑老鼠的年龄的,并不是单纯的将现存的老鼠直接-1就可以的,必须是年龄到3个月的时候才可-1。那就说明,这种状态方程计算数目的时候,必须要引入老鼠的年龄的概念。
  • 这里的动态转移方程:已经想到这个题目应该是可以利用动态规划的思想去解决的
3
  • 从老鼠年龄的角度出发,记录不同的月份的老鼠的有多少只,岛上的老鼠数目,就是当前n个月,不同月份的老鼠数目和

思路图解

  • 下文提到的如(1,2)表示,1个月后,年龄为2两个月的老鼠数目
0个月时

0个月
其实就是新生老鼠来到岛上的时候,只存在x只新生的老鼠

1个月时

1个月时

  • 约束:
    • (1,1)的老鼠,只能由0个月时的0个月的老鼠(0,0)成长而来,计算时候只需要考虑(0,0)格子(当前月份的老鼠,只能由上一个月的老鼠成长而来)
    • 同理,(1,2)有(0,1)
    • 没有新生,(1,0)为0
2个月时2个月时
  • 约束:
    • 岛上开始出现2个月的老鼠(1,1)经过1个月的成长具备生育能力,(2,0)为2*(1,1)
3个月时

3个月时

  • 约束:
    • 岛上开始出现3个月的老鼠,出现意外死亡,(3,3)=(2,2)-1
    • (3,0)为0,因为(2,1)为0,不存在具备生育能力的老鼠
    • (3,1),(3,2)分别由(2,0),(2,1)成长而来
4个月时

4个月时

  • 约束:
    • (3,3)经过一个月的成长,又具备生育能力了(每两个月),因此(4,0)=2*(3,1)+2*(3,3)
5个月时

5个月时

  • 约束:
    • 正常死亡,(5,5)由(4,4)成长而来,但是会死亡,所以(5,5)为0
    • 其实这个状态是可以没有的,因为(n,5)恒为0,不存在5个月的老鼠,但是这里为了体现死亡状态,特地把这一行写出来

状态转移方程

得出状态转移方程

  • 0个月 f0(n) = 2f1(n-1)+2f3(n-1) //1个月,3个月的老鼠剩余

  • 1个月 f1(n) = f0(n-1) //0个月的老鼠成长

  • 2个月 f2(n) = f1(n-1) //1个月的老鼠成长

  • 3个月 f3(n) = f2(n-1)-1 假设f2(n-1)>0 //2个月的老鼠成长,意外死亡1对

  • 4个月 f4(n) = f3(n-1) //3个月的老鼠成长

  • 5个月 f5(n) = 0 //4个月成长的老鼠,死亡

  • 总的老鼠:total[n] = f0+f1+f2+f3+f4+f5

// f0(n) n个月时,0个月的老鼠有几只
// 从上面其实可以看出,再强调一次,第n个月老鼠数目,只与n-1个月时的不同月份的老鼠有关(后面代码优化的时候会用到的)

一个二维数组记录n+1行,6列,行数表示当前时间,列数表述当前n个月,不同年龄的老鼠array[n+1][6]


0个月时,[x,0,0,0,0,0]
1个月时,根据array[0]进行计算
2个月时,根据array[1]进行计算
3个月时,根据array[2]进行计算
.....
  • **总的老鼠:total[n] = array[n][0]+array[n][1]+array[n][2]+array[n][3]+array[n][4]+array[n][5]

空间复杂度优化

  • 其实,思路解析到这里,如果有接触过01背包问题的同学,已经感觉到非常熟悉了,01背包问题的优化,最后会有存在对空间复杂度的优化
  • 这里用二维数组进行记录,array[n]的状态,只跟array[n-1]有关,其实不需要O(n)的空间复杂度的
  • 只需要array[2][6]的数组即可!
0个月时,[x,0,0,0,0,0]
1个月时,根据array[0]进行计算(跟前面一样)

2个月时,根据array[1]进行计算,记录在array[0]
        (因为,原先的array[0]已经成长为array[1]了,array[0]已经是废弃数据了,因此原先的array[2]数据可以记录在array[0],上一个月数据就是array[1])

3个月时,根据array[0]进行计算,记录在array[1]
.....

实际写代码的时候,只要当前月份除2取余数,得出记录月份以及上一月的计算基础

代码

  • 阿里的测评,对面试者,并没有类似leetcode提交代码,看是否ac。而是给出思路,手写代码。所以这边只提供思路,不提供解题代码~(其实,上面的分析,理解完,代码已经非常清晰了)
  • 我自己用js将上面的状态转移方程呈现出来,以及考虑优化,代码在50行以内
  • 大家可以用自己熟悉的语言写写看

last

不清楚上面是否有遗漏的case,欢迎交流,提供不一样的解题思路~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值