剑指Offer典型题整理 - 争取做最好的题解
整理时间:2020年02月20日
1 题目描述
把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]
2 题解
一开始我以为这是一道数学题,想着找找规律,Emmmm,然鹅并没什么规律。后来发现这是一道动态规划题目,一直以为动态规划的题目主要用来求最XX问题的,所以没有往DP上想,看样子动态规划学的还是不太行。
2.1 定义dp与初始化
如果用二维数组实现DP,就需要定义一个行数为 n + 1 n+1 n+1,列数为 6 ∗ n + 1 6*n+1 6∗n+1的矩阵,dp[i][j]表示使用i个色子出现的和为j的次数。并且将下标为1的那一行初始化:
dp = [[0 for i in range(6 * n + 1)] for j in range(n + 1)]
for i in range(1, 7):
dp[1][i] = 1
2.2 定义状态转移方程
这个问题之所以能够使用DP解决,就是因为状态可以叠加:k个色子求和的结果和k-1个色子有关。当有k-1个骰子时,再增加一个骰子,这个骰子的点数只可能为1、2、3、4、5或6。在k-1个骰子的基础上,再增加一个骰子出现点数和为n只有 下面6中情况:
(k-1,n-1):第k个骰子投了点数1
(k-1,n-2):第k个骰子投了点数2
(k-1,n-3):第k个骰子投了点数3
…
(k-1,n-6):第k个骰子投了点数6
所以:f(k,n)=f(k-1,n-1)+f(k-1,n-2)+f(k-1,n-3)+f(k-1,n-4)+f(k-1,n-5)+f(k-1,n-6)
2.3 代码实现
C++代码
class Solution {
public:
vector<double> twoSum(int n) {
vector<vector<int>> dp(n + 1);
for (int i = 0; i <= n; i++) {
dp[i].resize(6 * n + 1);
}
for (int i = 1; i <= 6; i++) {
dp[1][i] = 1;
}
for (int i = 2; i <= n; i++) {
for (int j = i; j < 6 * i + 1; j++) {
for (int k = 1; k <= 6; k++) {
if (j - k > 0) {
dp[i][j] += dp[i - 1][j - k];
}
}
}
}
vector<double> ans;
float base = pow(6, n);
for (int i = n; i <= 6 * n; i++) {
ans.push_back(dp[n][i] / base);
}
return ans;
}
};
python代码
class Solution(object):
def twoSum(self, n):
dp = [[0 for i in range(6 * n + 1)] for j in range(n + 1)]
for i in range(1, 7):
dp[1][i] = 1
for i in range(2, n + 1):
for j in range(i, 6 * i + 1):
for k in range(1, 7):
if j - k > 0:
dp[i][j] += dp[i - 1][j - k]
base = math.pow(6, n)
return [counts / float(base) for counts in dp[n][n:]]
(完)