题解 | #放苹果#

放苹果

http://www.nowcoder.com/practice/bfd8234bb5e84be0b493656e390bdebf

题目分析

  1. 题目给我们苹果的数量m和盘子的数量n
  2. 要将苹果放在盘子里,可以允许空盘,问有多少种放置方案
  3. 但是盘子顺序是可以任意调换的,调换前后认为是同一种放置方案

方法一:递归

  • 实现思路
    • 我们递归的最终结果是要返回solution(m,n)
    • 递归退出条件:
      • 如果没有苹果,则说明放置已经完成,方案已定,只能为1
      • 如果有一个苹果,则放在剩下哪一个盘子中都是一样的,因此方案数也是1
      • 如果只有一个盘子,则手上的苹果必须全部放在这一个盘子中,因此方案数也是1
    • 如果盘子数量大于苹果数量
      • 则必定有n-m个盘子空着,所以必定的事件不会影响方案数
    • 如果盘子数量小于等于苹果数量
      • 则考虑是不是要放满所有的盘子

      • 如果要空一个盘子,则有solution(m,n-1)种方案

      • 如果不要空这些盘子,则相当于所有盘子里至少会放一个苹果,那就相当于每个盘子都拿掉一个苹果,放了也白放不影响方案数量,因此有solution(m-n,n)种方案




def solution(m, n):
    if m == 0 or m == 1 or n == 1:                  # 如果没有苹果了或只有1个苹果,则方案数返回1;如果只有一个盘子了,剩下的苹果必须全放盘子里,因此也只剩下一种方案
        return 1
    elif n > m:                                     # 如果盘子数量多于苹果,说明必定有n-m个盘子会空着,因此必定的事情不会影响方案数
        return solution(m, m)
    else:                                           # 如果苹果多,则要考虑盘子中是否要放苹果,如果空一个,则要递归(m,n-1);如果不空,则相当于所有盘子都要放至少一个苹果,这个条件等价于所有盘子都少掉一个苹果的方案数
        return solution(m, n-1) + solution(m-n, n)
    return
    

while True:
    try:
        m, n = map(int, input().split())
        print(solution(m, n))
    except:
        break

复杂度分析

  • 时间复杂度: O(2n)O(2^n ),画出递归树,执行时间复杂度即为2n2^n (同斐波拉契数列)
  • 空间复杂度:O(n)O(n),递归栈的深度。

方法二:动态规划

  • 实现思路
    • 我们将前面的递归思路进行迭代化得到动态规划的解法
    • 边缘节点处理
      • 如果没有苹果,则说明放置已经完成,方案已定,只能为1
      • 如果有一个苹果,则放在剩下哪一个盘子中都是一样的,因此方案数也是1
      • 如果只有一个盘子,则手上的苹果必须全部放在这一个盘子中,因此方案数也是1
    • 动态规划数组处理
    • 如果盘子数量大于苹果数量
      • 则必定有n-m个盘子空着,所以必定的事件不会影响方案数
    • 如果盘子数量小于等于苹果数量
      • 则考虑是不是要放满所有的盘子
      • 如果要空一个盘子,则有solution(m,n-1)种方案
      • 如果不要空这些盘子,则相当于所有盘子里至少会放一个苹果,那就相当于每个盘子都拿掉一个苹果,放了也白放不影响方案数量,因此有solution(m-n,n)种方案
  • 定义动态规划数组dp[i][j]的含义为对于i个苹果和j个盘子有多少种放置方案
  • 我们分成两种情况分析:
    • i<ji < j时,苹果数量比盘子数量少,则盘子一定会空,因此当前的方案数有转移方程dp[i][j]=dp[i][i]dp[i][j] = dp[i][i]
    • i>ji > j时,苹果数量多于盘子数量,则要考虑是否每个盘子都要装苹果,如果不装满所有的盘子,相当于盘子数量少一个,则因该取值dp[i][j1]dp[i][j-1],如果装满所有的盘子,则所有盘子里面至少有一个苹果,相当于所有盘子里面都去掉一个苹果再进行分配,此时应该取值dp[ij][j]dp[i-j][j],因此则有转移方程dp[i][j]=dp[ij][j]+dp[i][j1]dp[i][j] = dp[i-j][j] + dp[i][j-1]
  • 综上转移方程为 dp[i][j]={dp[i][i]if(i<j)dp[ij][j]+dp[i][j1]if(ij)dp[i][j]=\left\{ \begin{aligned} &dp[i][i] & if(i<j) \\ &dp[i-j][j] + dp[i][j-1] & if( i \ge j)\\ \end{aligned} \right.

alt



def solution(m, n):
    dp = [[0 for i in range(n+1)] for j in range(m+1)]
    for i in range(m+1):
        dp[i][1] = 1            # 如果只有一个盘子则只有一种放置方案
    for j in range(1, n+1):
        dp[0][j] = 1            # 如果没有苹果则只有一种放置方案
        dp[1][j] = 1            # 如果只有一个苹果也相当于只有一种方案
    for i in range(2, m+1):
        for j in range(2, n+1):
            if i < j:                                # 如果苹果数量少,则盘子一定会空,所以去除那些空的盘子其实不影响方案数
                dp[i][j] = dp[i][i]
            else:                                    # 如果苹果数量多,则考虑是否要装够j个盘子,如果不装够则有dp[i][j-1],如果装够则相当于从所有盘子同时去掉一个苹果无影响,则有dp[i-j][j]
                dp[i][j] = dp[i-j][j] + dp[i][j-1]
    
    return dp[m][n]
    
while True:
    try:
        m, n = map(int, input().split())
        print(solution(m, n))
    except:
        break

复杂度分析

  • 时间复杂度:O(mn)O(mn),双重循环的时间开销
  • 空间复杂度:O(mn)O(mn),动态规划矩阵的空间开销
全部评论
妈的,真鸡儿抽象
5 回复 分享
发布于 2022-05-20 11:17
如果盘子数量小于等于苹果数量的时候,不可以空几个盘子么?只能空一个?
3 回复 分享
发布于 2022-03-17 04:54
递归YYDS!
3 回复 分享
发布于 2022-02-23 20:49
抽象派
1 回复 分享
发布于 2023-03-27 19:13 北京
怎么保证去除重复摆放的情况了?
1 回复 分享
发布于 2022-07-21 11:34
牛逼又抽象
点赞 回复 分享
发布于 02-26 17:47 湖北
“简单”
点赞 回复 分享
发布于 2024-09-10 10:09 北京
'简单'
点赞 回复 分享
发布于 2024-05-19 11:37 浙江
毕竟暴力DFS的复杂度是O(2^N),上面的递归的复杂度会低一点吧?
点赞 回复 分享
发布于 2023-01-12 16:15 江苏
能解释下 这句么 苹果数量多于盘子数量,则要考虑是否每个盘子都要装苹果,如果不装满所有的盘子,相当于盘子数量少一个
点赞 回复 分享
发布于 2022-09-12 00:47 广东
看上面dp 表中有一个疑问 苹果数为1 盘子为1个时候 方案不是两个么 要么空着 要么放一个 题目也没说不能全空。难道是题目隐形条件
点赞 回复 分享
发布于 2022-04-06 10:49
如果m = 0, 第7行 dp[1][j]不会报错?
点赞 回复 分享
发布于 2021-12-13 10:35

相关推荐

Lorn的意义:你这种岗位在中国现在要么牛马天天加班,要么关系户进去好吃好喝,8年时间,真的天翻地覆了,对于资本来说你就说一头体力更好的牛马,哎,退伍没有包分配你真的亏了。
点赞 评论 收藏
分享
吴offer选手:下午mt一来就告警说项目来不及,估计明天拿了权限就要参与开发了 已老实
实习生的蛐蛐区
点赞 评论 收藏
分享
评论
167
66
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务