问题描述

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 <= 5000
  • rollMax.length == 6
  • 1 <= 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_lenmax_len + 1;否则必须选其他的数,同时内层递归的max_len置1;

当前递归的结果应该取下一层递归的结果之和;

边界条件,当idx == 0时,应该return 1;

cache数组的三个维度即idxlast_nummax_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)的更多相关文章

  1. LeetCode 1223. 掷骰子模拟 Dice Roll Simulation - Java - DP

    题目链接:1223. 掷骰子模拟 有一个骰子模拟器会每次投掷的时候生成一个 1 到 6 的随机数. 不过我们在使用它时有个约束,就是使得投掷骰子时,连续 掷出数字 i 的次数不能超过 rollMax[ ...

  2. python应用-掷骰子模拟-pygal

    pygal安装: Linux下: pip install pygal Windows下: python -m pip install pygal 效果如图: # -*- coding: utf-8 - ...

  3. Python绘制直方图 Pygal模拟掷骰子

    #coding=utf-8 from random import randint class Die(): """骰子类""" def __ ...

  4. 使用python实现模拟掷骰子数据分析

    Data:2020/4/8 主题:模拟实现掷骰子数据分析 编译环境:pycharm 库:pygal 说明: code 1:创建一个掷骰子类对象,类方法获得掷骰子随机数1-6,默认6个面,模拟20次将结 ...

  5. Python Tkinter小实例——模拟掷骰子

    什么是Tkinter? Tkinter 是 Python 的标准 GUI 库.Python 使用 Tkinter 可以快速的创建 GUI 应用程序. 由于 Tkinter 是内置到 python 的安 ...

  6. Python 使用matplotlib模块模拟掷骰子

    掷骰子 骰子类 # die.py 骰子类模块 from random import randint class Die(): """骰子类""&quo ...

  7. 【NOIP2012模拟10.31】掷骰子

    题目 太郎和一只免子正在玩一个掷骰子游戏.有一个有N个格子的长条棋盘,太郎和兔子轮流掷一个有M面的骰子,骰子M面分别是1到M的数字.且掷到任意一面的概率是相同的.掷到几.就往前走几步.当谁走到第N格时 ...

  8. TurnipBit开发板掷骰子小游戏DIY教程实例

    转载请以链接形式注明文章来源(MicroPythonQQ技术交流群:157816561,公众号:MicroPython玩家汇) 0x00前言 下面带大家用TurnipBit开发板实现一个简单的小游戏- ...

  9. python-模拟掷骰子,两个筛子数据可视化

    """ 作者:zxj 功能:模拟掷骰子,两个筛子数据可视化 版本:3.0 日期:19/3/24 """ import random impo ...

  10. Pygal之掷骰子

    python之使用pygal模拟掷骰子创建直方图: 1,文件die.py,源码如下: 1 from random import randint 2 3 class Die(): 4 '''表示一个骰子 ...

随机推荐

  1. python 爬站长素材网页图片

    一.我们要用python第三方库: import requests import re 二.找到自己感兴趣的网页图片: for i in range(1,2): url = "https:/ ...

  2. 正确理解和使用JAVA中的字符串常量池

    前言 研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池.Java中的字符串常量池是Java堆中的一块特殊存储区域,用于存储字符串.它的实 ...

  3. cookie、session,、token,还在傻傻分不清?

    摘要:session 和 token 本质上是没有区别的,都是对用户身份的认证机制,只是他们实现的校验机制不一样而已. 本文分享自华为云社区<Session/Cookie/Token 还傻傻分不 ...

  4. js 禁用刷新快捷键

    // 上代码 /** * 按键监听 * Ctrl-17,F5-116,R-82 */ var oldKeyCode = -1; document.onkeydown = function (e) { ...

  5. MySQL优化四,高性能优化

    一,查询优化器 这个部分的整个过程是由MySQL的存储引擎来做的,优化器就会根据存储引擎来使用原来的开销, 优化后的开销,哪个更好一点? 1.如果是查询语句(select语句),首先会查询缓存是否已有 ...

  6. 刷题笔记——1267.A+B Problem

    题目 1267.A+B Problem 代码 while True: try: a,b=map(int,input().strip().split()) print(a+b) except: brea ...

  7. 个别编程语言在OJ平台上的输入输出方式

    OJ 平台输入输出 一. C/ C++ 输入 scanf() gets() getchar() 输出 printf() puts() putchar() C++ cin >> cout & ...

  8. DLL的两种加载方式

    案例简述 在某项目中,需要使用两个不同版本的HCNetSDK库,我们通常使用的静态加载DLL的方式不能满足该需求,故用到动态加载DLL的方式. 背景技术及术语解释 静态加载:也称隐式调用,指在运行程序 ...

  9. 11月21日内容总结——多进程实现TCP服务端并发、互斥锁、线程及代码实现、GIL全局解释器锁、信号量、event事件、进程池和线程池、协程

    目录 一.多进程实现TCP服务端并发 二.互斥锁代码实操 1.互斥锁的概念 2.互斥锁的使用 3.死锁现象 4. 小结 三.线程理论 进程 线程 线程简介 为什么要使用多线程? 多线程概念 多进程的优 ...

  10. Python 发展趋势:与 Rust 深度融合、更易于编写 Web 应用

    大家好,我是猫哥,好久不见!2022 年末的时候,我不可避免地阳了,借着身体不舒服就停更了,接踵而至的是元旦和春节假期,又给自己放了假,连年终总结也鸽了,一懈怠就到了 2 月中旬-- 现在是我家娃出生 ...