ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

808 分汤(递归、二维动态规划)

2021-04-05 17:01:10  阅读:178  来源: 互联网

标签:25 概率 return 递归 808 分汤 分配 dp


1. 问题描述:

有 A 和 B 两种类型的汤。一开始每种类型的汤有 N 毫升。有四种分配操作:
提供 100ml 的汤A 和 0ml 的汤B。
提供 75ml 的汤A 和 25ml 的汤B。
提供 50ml 的汤A 和 50ml 的汤B。
提供 25ml 的汤A 和 75ml 的汤B。
当我们把汤分配给某人之后,汤就没有了。每个回合,我们将从四种概率同为0.25的操作中进行分配选择。如果汤的剩余量不足以完成某次操作,我们将尽可能分配。当两种类型的汤都分配完时,停止操作。注意不存在先分配100 ml汤B的操作。需要返回的值: 汤A先分配完的概率 + 汤A和汤B同时分配完的概率 / 2。

示例:
输入: N = 50
输出: 0.625
解释:
如果我们选择前两个操作,A将首先变为空。对于第三个操作,A和B会同时变为空。对于第四个操作,B将首先变为空。
所以A变为空的总概率加上A和B同时变为空的概率的一半是 0.25 *(1 + 1 + 0.5 + 0)= 0.625。

注释:

  • 0 <= N <= 10^9
  • 返回值在 10^-6 的范围将被认为是正确的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/soup-servings

2. 思路分析:

① 分析题目可以知道每一次对于当前存在A类型的汤x毫升、B类型的汤y毫升有四种可能选择的方案,如果选择其中一种方案那么选择这种方案的概率就为1 / 4,而我们需要计算的是:汤A先分配完的概率 + 汤A和汤B同时分配完的概率 / 2,所以需要将选择四种方案中的每一种方案中汤A先分配完的概率 + 汤A和汤B同时分配完的概率 / 2的结果累加起来,因为存在四种可能选择的方案,所以最容易想到的是使用递归的方法进行解决,在递归的方法体中对应着四个递归的方法。从题目中可以知道分配的操作中汤的体积都为25的倍数,所以为了节省空间,将分配方案中A/B类型的汤各自除以25,并且对一开始汤的类型的体积也除以25(向上取整),当A <= 0并且B <= 0时那么表示AB同时分完了,返回0.5,与题目中汤A和汤B同时分配完的概率 / 2对应,当A <= 0时表明A先分完这个时候返回1.0,当B <= 0时那么应该表明B先分完,如果AB还没有分完继续递归下去,并且从控制台输入的N大概是10000的时候好像概率与1的差距小于10 ^-6(通过提交代码看哪一个结果与1的差距在10^-16以内即可)。因为涉及到四种方案的选择,为了避免重复性的递归,使用了字典来记录已经求解过的值,也即记忆型的递归,因为递归方法中只涉及到两个动态变化的参数,所以只需要记录AB两个参数即可,可以将AB作为一个元组元素存储到字典中即可,当当前这一层的递归完成之后那么将递归的结果存储到对应的字典中并且进行返回即可

② 由①可以知道其实这道题目为组合问题,我们可以根据①中的分析将其优化为二维动态规划问题。可以声明一个长度为n的二维列表(n = N // 25向上取整),dp[i][j]表示当前A类型的汤剩余i毫升,B类型的汤剩余j毫升的情况下的汤A先分配完的概率 + 汤A和汤B同时分配完的概率 / 2的概率,使用两层循环进行递推即可,其实与递归的时候是类似的

3. 代码如下:

递归:

import collections


class Solution:
    # 记忆型的递归: 使用字典记录求解过的值
    def dfs(self, A: int, B: int, rec):
        if (A, B) in rec: return rec[(A, B)]
        if A <= 0 and B <= 0:
            # AB同时分完那么概率为1 / 2(与题目的测试用例的计算方式是一致的)
            return 0.5
        elif A <= 0:
            return 1.0
        elif B <= 0:
            return 0.0
        else:
            rec[(A, B)] = 0.25 * (
                        self.dfs(A - 4, B, rec) + self.dfs(A - 3, B - 1, rec) + self.dfs(A - 2, B - 2, rec) + self.dfs(A - 1, B - 3, rec))
            return rec[(A, B)]

    def soupServings(self, N: int) -> float:
        if N >= 10000: return 1.0
        rec = collections.defaultdict(tuple)
        N = N // 25 if N % 25 == 0 else N // 25 + 1
        return self.dfs(N, N, rec)

动态规划:

class Solution:
    # 转换为二维动态规划解决即可
    def soupServings(self, N: int) -> float:
        if N == 0: return 0.5
        if N >= 10000: return 1.0
        choose = [(4, 0), (3, 1), (2, 2), (1, 3)]
        N = N // 25 if N % 25 == 0 else N // 25 + 1
        dp = [[0] * (N + 1) for i in range(N + 1)]
        dp[0][0] = 0.5
        for i in range(1, N + 1):
            dp[i][0] = 0.0
            dp[0][i] = 1.0
        for i in range(1, N + 1):
            for j in range(1, N + 1):
                for k in range(4):
                    x = max(0, i - choose[k][0])
                    y = max(0, j - choose[k][1])
                    dp[i][j] += dp[x][y]
                dp[i][j] *= 0.25
        return dp[N][N]

 

标签:25,概率,return,递归,808,分汤,分配,dp
来源: https://blog.csdn.net/qq_39445165/article/details/115443537

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有