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 '''表示一个骰子 ...
随机推荐
- python 爬站长素材网页图片
一.我们要用python第三方库: import requests import re 二.找到自己感兴趣的网页图片: for i in range(1,2): url = "https:/ ...
- 正确理解和使用JAVA中的字符串常量池
前言 研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池.Java中的字符串常量池是Java堆中的一块特殊存储区域,用于存储字符串.它的实 ...
- cookie、session,、token,还在傻傻分不清?
摘要:session 和 token 本质上是没有区别的,都是对用户身份的认证机制,只是他们实现的校验机制不一样而已. 本文分享自华为云社区<Session/Cookie/Token 还傻傻分不 ...
- js 禁用刷新快捷键
// 上代码 /** * 按键监听 * Ctrl-17,F5-116,R-82 */ var oldKeyCode = -1; document.onkeydown = function (e) { ...
- MySQL优化四,高性能优化
一,查询优化器 这个部分的整个过程是由MySQL的存储引擎来做的,优化器就会根据存储引擎来使用原来的开销, 优化后的开销,哪个更好一点? 1.如果是查询语句(select语句),首先会查询缓存是否已有 ...
- 刷题笔记——1267.A+B Problem
题目 1267.A+B Problem 代码 while True: try: a,b=map(int,input().strip().split()) print(a+b) except: brea ...
- 个别编程语言在OJ平台上的输入输出方式
OJ 平台输入输出 一. C/ C++ 输入 scanf() gets() getchar() 输出 printf() puts() putchar() C++ cin >> cout & ...
- DLL的两种加载方式
案例简述 在某项目中,需要使用两个不同版本的HCNetSDK库,我们通常使用的静态加载DLL的方式不能满足该需求,故用到动态加载DLL的方式. 背景技术及术语解释 静态加载:也称隐式调用,指在运行程序 ...
- 11月21日内容总结——多进程实现TCP服务端并发、互斥锁、线程及代码实现、GIL全局解释器锁、信号量、event事件、进程池和线程池、协程
目录 一.多进程实现TCP服务端并发 二.互斥锁代码实操 1.互斥锁的概念 2.互斥锁的使用 3.死锁现象 4. 小结 三.线程理论 进程 线程 线程简介 为什么要使用多线程? 多线程概念 多进程的优 ...
- Python 发展趋势:与 Rust 深度融合、更易于编写 Web 应用
大家好,我是猫哥,好久不见!2022 年末的时候,我不可避免地阳了,借着身体不舒服就停更了,接踵而至的是元旦和春节假期,又给自己放了假,连年终总结也鸽了,一懈怠就到了 2 月中旬-- 现在是我家娃出生 ...