1223. 掷骰子模拟 (Hard)
问题描述
有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数。
不过我们在使用它时有个约束,就是使得投掷骰子时, 连续 掷出数字 i 的次数不能超过
rollMax[i]( i 从 1 开始编号)。
现在,给你一个整数数组 rollMax 和一个整数 n,请你来计算掷 n 次骰子可得到的不同点数序列的数量。
假如两个序列中至少存在一个元素不同,就认为这两个序列是不同的。由于答案可能很大,所以请返回 模 10^9 + 7 之后的结果。
示例 1:
输入:n = 2, rollMax = [1,1,2,2,2,3]
输出:34
解释:我们掷 2 次骰子,如果没有约束的话,共有 6 * 6 = 36 种可能的组合。但是根据 rollMax
数组,数字 1 和 2 最多连续出现一次,所以不会出现序列 (1,1) 和 (2,2)。因此,最终答案是 36-2 =
34。
示例 2:
输入:n = 2, rollMax = [1,1,1,1,1,1]
输出:30
示例 3:
输入:n = 3, rollMax = [1,1,1,2,2,3]
输出:181
提示:
1 <= n <= 5000rollMax.length == 61 <= rollMax[i] <= 15
解题思路
动态规划
这一题是很明显的动态规划思路,状态定义很好想,状态转移方程也不难确定:
记dp[i][j]为掷i次骰子,最后一次结果为j + 1的可能的组合,要求的答案即dp[n][0] + ... + dp[n][5],显然,我们只需排除最后rollMax[j] + 1次都是j的情况;
状态转移方程为:
if i <= rollMax[j]: \(dp[i][j] = \sum\limits_{k = 0}^{5} dp[i - 1][k]\)
else if i == rollMax[j] + 1: \(dp[i][j] = \sum\limits_{k = 0}^{5} dp[i - 1][k] - 1\)
else: \(dp[i][j] = \sum\limits_{k = 0}^{5}dp[i - 1][k] - \sum\limits_{k = 0, k\neq j}^{5}dp[i - rollMax[j] - 1][j]\)
记忆化搜索
这里我们从第n次向第一次倒着考虑,那么我们要关注的几个变量:
- 当前投掷骰子的次数
idx; - 当前投掷的骰子数字减一,记为
last_num; - 当前数字往后的最大连续数量,记为
max_len,例如\(623166\),考虑idx = 5,那么最大连续数量就是\(2\),这里的max_len要分等于rollMax[last_num]和小于rollMax[last_num]的情况来讨论,小于则可以继续选last_num,内层递归的max_len为max_len + 1;否则必须选其他的数,同时内层递归的max_len置1;
当前递归的结果应该取下一层递归的结果之和;
边界条件,当idx == 0时,应该return 1;
cache数组的三个维度即idx,last_num,max_len,其中max_len <= 15,所以cache数组为vector<vector<vector<long>>> cache(n + 1, vector<vector<long>>(16, vector<long>(6, -1)));
代码
动态规划
class Solution {
public:
int dieSimulator(int n, vector<int> &rollMax) {
int mod = 1000000007;
if (n == 1) return 6;
vector<vector<int>> dp(n + 1, vector<int>(6, 0));
for (int i = 0; i < 6; i++) {
dp[0][i] = 1;
dp[1][i] = 1;
}
for (int i = 2; i <= n; i++) {
for (int j = 0; j < 6; j++) {
if (i <= rollMax[j]) {
dp[i][j] = (dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][3] + dp[i - 1][4] + dp[i - 1][5]) % mod;
} else if (i == rollMax[j] + 1) {
int tmp_sum = 0;
for (int k = 0; k < 6; k++) {
tmp_sum = (tmp_sum + dp[i - 1][k]) % mod;
}
dp[i][j] = (tmp_sum - 1) % mod;
} else {
int tmp_sum = 0;
int tmp_minus = 0;
for (int k = 0; k < 6; k++) {
tmp_sum = (tmp_sum + dp[i - 1][k]) % mod;
if (k == j) {
continue;
}
tmp_minus = (tmp_minus + dp[i - rollMax[j] - 1][k]) % mod;
}
dp[i][j] = (tmp_sum - tmp_minus + mod) % mod;
}
}
}
int res = 0;
for (int j = 0; j < 6; j++) {
res = (res + dp[n][j]) % mod;
}
return res;
}
};
记忆化搜索
class Solution {
public:
long dfs(int idx, vector<int> &rollMax, int max_len, int last_num, vector<vector<vector<long>>> &cache, int mod) {
if (idx == 0) {
return 1;
}
if (cache[idx][max_len][last_num] >= 0) {
return cache[idx][max_len][last_num] % mod;
}
long res = 0;
if (max_len < rollMax[last_num]) {
for (int i = 0; i < 6; ++i) {
if (i == last_num) {
res += dfs(idx - 1, rollMax, max_len + 1, i, cache, mod);
res %= mod;
} else {
res += dfs(idx - 1, rollMax, 1, i, cache, mod);
res %= mod;
}
}
} else {
for (int i = 0; i < 6; ++i) {
if (i != last_num) {
res += dfs(idx - 1, rollMax, 1, i, cache, mod);
res %= mod;
}
}
}
cache[idx][max_len][last_num] = res % mod;
return cache[idx][max_len][last_num];
}
int dieSimulator(int n, vector<int> &rollMax) {
// 尝试记忆化搜索
vector<vector<vector<long>>> cache(n + 1, vector<vector<long>>(16, vector<long>(6, -1)));
int mod = 1000000007;
return dfs(n, rollMax, 0, 6, cache, mod);
}
};
1223. 掷骰子模拟 (Hard)的更多相关文章
- LeetCode 1223. 掷骰子模拟 Dice Roll Simulation - Java - DP
题目链接:1223. 掷骰子模拟 有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数. 不过我们在使用它时有个约束,就是使得投掷骰子时,连续 掷出数字 i 的次数不能超过 rollMax[ ...
- python应用-掷骰子模拟-pygal
pygal安装: Linux下: pip install pygal Windows下: python -m pip install pygal 效果如图: # -*- coding: utf-8 - ...
- Python绘制直方图 Pygal模拟掷骰子
#coding=utf-8 from random import randint class Die(): """骰子类""" def __ ...
- 使用python实现模拟掷骰子数据分析
Data:2020/4/8 主题:模拟实现掷骰子数据分析 编译环境:pycharm 库:pygal 说明: code 1:创建一个掷骰子类对象,类方法获得掷骰子随机数1-6,默认6个面,模拟20次将结 ...
- Python Tkinter小实例——模拟掷骰子
什么是Tkinter? Tkinter 是 Python 的标准 GUI 库.Python 使用 Tkinter 可以快速的创建 GUI 应用程序. 由于 Tkinter 是内置到 python 的安 ...
- Python 使用matplotlib模块模拟掷骰子
掷骰子 骰子类 # die.py 骰子类模块 from random import randint class Die(): """骰子类""&quo ...
- 【NOIP2012模拟10.31】掷骰子
题目 太郎和一只免子正在玩一个掷骰子游戏.有一个有N个格子的长条棋盘,太郎和兔子轮流掷一个有M面的骰子,骰子M面分别是1到M的数字.且掷到任意一面的概率是相同的.掷到几.就往前走几步.当谁走到第N格时 ...
- TurnipBit开发板掷骰子小游戏DIY教程实例
转载请以链接形式注明文章来源(MicroPythonQQ技术交流群:157816561,公众号:MicroPython玩家汇) 0x00前言 下面带大家用TurnipBit开发板实现一个简单的小游戏- ...
- python-模拟掷骰子,两个筛子数据可视化
""" 作者:zxj 功能:模拟掷骰子,两个筛子数据可视化 版本:3.0 日期:19/3/24 """ import random impo ...
- Pygal之掷骰子
python之使用pygal模拟掷骰子创建直方图: 1,文件die.py,源码如下: 1 from random import randint 2 3 class Die(): 4 '''表示一个骰子 ...
随机推荐
- uniapp 微信小程序 实现左侧菜单右侧列表,双向联动的效果
<template> <view class="u-wrap"> <view class="u-search-box"> & ...
- JavaScript:变量的作用域,window对象,关键字var/let与function
为什么要将这些内容放在一起,因为他们都跟初始化有关系,我们慢慢说吧. 我们在代码中,都会声明变量.函数和对象,然后由浏览器解释器(下面简称浏览器)执行: 我们还说过,变量和对象的内存结构: 那么,是什 ...
- 2022年7月13日,第四组 周鹏 JAVA认识的第一天,附加一个用JS写的计算器代码
心情:╭(╯^╰)╮ ╮(╯﹏╰)╭ (╯﹏╰)b 罒ω罒 |*´Å`)ノ ( Ĭ ^ Ĭ ) (ㄒoㄒ) o(╥﹏╥)o /(ㄒoㄒ)/~~ (〒︿〒) ┭┮﹏┭┮ ε(┬┬﹏┬┬)3 ε(┬┬﹏┬ ...
- Transition 初步使用
Transition Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡: 条件渲染 (使用 v-if) 条件展示 (使用 v-show) 动态组件 ...
- APICloud AVM框架列表组件list-view的使用、flex布局教程
avm.js 是APICloud 推出的多端开发框架.使用 avm.js 一个技术栈可同时开发 Android & iOS 原生 App.小程序和 iOS 轻 App,且多端渲染效果统一:全新 ...
- pulsar集群搭建——生产环境
pulsar集群搭建 前置条件,需要JDK环境 192.168.1.1 192.168.1.2 192.168.1.3 写入hosts 所有机器都需要执行 $ cat >>/etc/hos ...
- [深度学习] Python人脸识别库face_recognition使用教程
Python人脸识别库face_recognition使用教程 face_recognition号称是世界上最简单的开源人脸识别库,可以通过Python或命令行识别和操作人脸.face_recogni ...
- Unity跑在Awake之前的方法
Unity跑在Awake之前的方法 一.前言 相信大家和小黑一样,在写项目的时候遇到过以下这中情况: ____两个脚本的Awake中,都有获取信息的函数被调用.可是A脚本在B脚本获取到信息之后,才可以 ...
- 【CodeSmith】The System.Data.SQLite library is not installed on this computer,不能使用SQLite解决办法
1.出现问题原因 1)System.Data.SQLite 尚未安装在您的计算机上 2)System.Data.SQLite 尚未正确配置 2.下载并安装System.Data.Sqlite [注意] ...
- Kinsoku jikou desu新浪股票接口变动
1.问题原因 新浪股票接口返回如标题所示值:Kinsoku jikou desu! http://hq.sinajs.cn/list=code 新浪股票的接口变动,需要在请求头中添加Referer值. ...