n个骰子的点数

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

需要用一个浮点数数组返回答案,其中第i个元素代表这n个骰子所能掷出的点数集合中第i小的那个的概率。

输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]

输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

思路:动态规划

设输入n个骰子的解(即概率列表)为f(n),其中 “点数和”x的概率为f(n, x)
假设已知n - 1个骰子的解f(n - 1),此时添加一枚骰子,求n个骰子的点数和为x的概率f(n,x)。
当添加骰子的点数为1时,前n-1个骰子的点数和应为x - 1,方可组成点数和x;同理,当此骰子为2时,前n - 1个骰子应为x - 2;以此类推,直至此骰子点数为6。将这6种情况的概率相加,即可得到概率f(n, x)。

f(n,x) =
i = 1
∑ [ ( f(n−1,x−i) ) × (1/6) ]
6
在这里插入图片描述
以上递推公式虽然可行,但f(n - 1, x - i)中的x - i会有越界问题。

如下图所示,以上递推公式是“逆向”的,即为了计算f(n, x),将所有与之有关的情况求和;而倘若改换为“正向”的递推公式,便可解决越界问题。

在这里插入图片描述
将f(i)记为动态规划列表形式dp[i],则i = 1, 2, …, n的状态转移过程如下图所示:
在这里插入图片描述
通常做法是声明一个二维数组dp,dp[i][j]代表前i个骰子的点数和j的概率,并执行状态转移。而由于dp[i]仅由dp[i - 1]递推得出,为降低空间复杂度,只建立两个一维数组dp,tmp交替即可。

class Solution {
public:
	vector<double> dicesProbability(int n){
		vector<double> dp(6, 1.0 / 6.0);
		for (int i = 2; i < = n; i++)
		{
			vector<double> tmp(5 * i + 1, 0);
			for (int j = 0; j < dp.size(); j++)
			{
				for (int k = 0; k < 6; k++)
				{
					tmp[j + k] += dp[j] / 6.0;
				}
			}
			dp = tmp;
		}
		return dp;
	}
};

在这里插入图片描述

如上图所示,在计算j = 1时,由于tmp第一个计算的就是tmp[1],后面新增加了一个tmp[6],于是可以形成滑动数组的形式。

复杂度分析

  • 时间复杂度:O(n
    2
    ) : 状态转移循环 n - 1n−1 轮;每轮中,当 i = 2, 3, …, ni=2,3,…,n 时,对应循环数量分别为 6 \times 6, 11 \times 6, …, [5(n - 1) + 1] \times 66×6,11×6,…,[5(n−1)+1]×6 ;因此总体复杂度为 O((n - 1) \times \frac{6 + [5(n - 1) + 1]}{2} \times 6)O((n−1)×
    2
    6+[5(n−1)+1]

    ×6) ,即等价于 O(n^2)O(n
    2
    )

  • 空间复杂度:状态转移过程中,辅助数组 tmp 最大长度为 6(n-1) - [(n-1) - 1] = 5n - 4,因此使用 O(5n - 4) = O(n)大小的额外空间。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值