大厂真题:【DP】米哈游2023秋招-米小游与魔法少女-奇运

2023-10-06 11:50

本文主要是介绍大厂真题:【DP】米哈游2023秋招-米小游与魔法少女-奇运,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述与示例

题目描述

米小游都快保底了还没抽到希儿,好生气哦!只能打会活动再拿点水晶。

米小游和世界第一可爱的魔法少女 TeRiRi 正在打 BOSS,BOSS 的血量为h,当 BOSS 血量小于等于0时,BOSS 死亡。TeRiRi 有一套牌,在一轮中,她会按顺序一张一张的将卡牌打出,套牌中有两种卡牌:

  1. 时来运转:获得x幸运币
  2. 幸运一掷:造成x点伤害,并投掷所有幸运币,造成等于所有幸运币掷出的点数之和的伤害。

幸运币可以等概率的投掷出1∼6之间的点数。 (所以为什么不叫骰子呢?)

米小游想知道,TeRiRi 的套牌在一轮内击杀 BOSS 的概率。

输入描述

第一行输入两个整数n (1≤n≤100)h (1≤h≤10^9),分别表示卡牌张数和 BOSS 血量。

接下来n行,每行首先输入两个整数t (1≤t≤2)x (1≤x≤10)t1表示卡牌为时来运转,t2表示卡牌为幸运一掷。

输出描述

输出一个实数表示答案,你的答案与标准答案的误差不超过10^−4都被认为是正确答案。

示例一

输入

2 5
1 1
2 1

输出

0.5

说明

幸运币掷出4及以上的概率为0.5,再加上1点固定伤害,即可击杀BOSS。

示例二

输入

3 1145
1 4
1 9
1 9

输出

0

说明

无论如何都无法击杀BOSS。

解题思路

对于固定顺序的套牌,投掷幸运币的数量是固定的。这里要注意的是,由于时来运转之后必须接上幸运一掷才能将幸运币打出造成伤害,所以如果最后的若干张连续的卡牌是时来运转,这些最后获得的幸运币也是无法造成伤害的。

我们将造成的伤害分为两部分,固定伤害和随机伤害,前者为打出y个幸运币必定造成的z点伤害,后者为y个幸运币掷出点数和的伤害。

假设整套卡牌一共投掷了y个幸运币,造成的固定伤害z点,如果想要击杀BOSS,随机伤害必须至少达到h-z点才可以。当然,如果h-z≤0,则必定可以击杀BOSS。

问题就转换为,投掷出y个幸运币,点数总和超过h-z的概率是多少?

由于每一个幸运币都是独立的,在掷出第i个幸运币时,其结果是从掷出第i-1个幸运币时得到的各种结果转移得到的,因此我们可以使用动态规划来解决该问题。我们考虑动态规划三部曲:

  1. dp数组的含义是什么?
  • dp数组是一个长度为(y+1)×(h-z+1)的二维矩阵,dp[i][j]表示掷出第i个幸运币时,有多大的概率可以取得和为j的结果,即造成和为j的伤害。
  • 特别地,由于只需要判断伤害之和大于等于h-z的概率,而不用关心具体的分布,dp数组内层的第h-z个元素,即dp[i][h-z],表示求和大于等于h-z的概率。
  1. 动态转移方程是什么?
  • 由于幸运币掷出点数1-6是等概率的,故对于某一个特定的dp[i-1][j],在掷出第i个幸运币时,dp[i-1][j]的结果将等概率地转换到dp[i][j+1]dp[i][j+2]dp[i][j+3]dp[i][j+4]dp[i][j+5]dp[i][j+6],即每一个状态都可以取得1/6的转移。
  • 另外,如果j+k之后超过了h-z,则将直接获得(7-k)/6 * dp[i-1][j]的概率。
for i in range(1, y+1):for j in range(i-1, h-z+1):for k in range(1, 7):if j + k >= h - z:dp[i][h-z] += (7-k)/6 * dp[i-1][j]breakelse:dp[i][j+k] += 1/6 * dp[i-1][j]
  1. dp数组如何初始化?
  • 考虑不投掷任何幸运币的情况,那么只有一种情况,也就是在投掷0个幸运币的时候获得求和为0的概率为恒定1。故初始化dp[0][0] = 1
dp = [[0] * (h-z+1) for _ in range(y+1)]
dp[0][0] = 1

考虑完上述问题后,代码其实呼之欲出了。

代码

Python

# 题目:【DP】米哈游2023秋招-米小游与魔法少女-奇运
# 作者:闭着眼睛学数理化
# 算法:DP
# 代码有看不懂的地方请直接在群上提问y = 0       # 掷出幸运币的总个数
z = 0       # 全部造成的固定伤害
x_temp = 0  # 时来运转获得的幸运币n, h = map(int, input().split())
for _ in range(n):t, x = map(int, input().split())# 时来运转if t == 1:x_temp += x# 幸运一掷else:y += x_tempx_temp = 0z += x# 如果固定伤害已经大于h,直接输出1
if h - z <= 0:print(1)
# 否则才需要进行dp过程
else:# 初始化dp数组# dp[i][j]表示掷出了i个幸运币时,# 有多大的概率可以取得和为j的结果,即造成和为j的伤害。dp = [[0] * (h-z+1) for _ in range(y+1)]dp[0][0] = 1# 考虑每一个幸运币for i in range(1, y+1):# 对于每一个幸运币考虑打出i-1个硬币后的# 每一种求和结果的概率# 注意,由于已经掷出了i-1个幸运币# 那么求和结果至少为i-1,因为每个幸运币点数至少为1点# 因此j遍历时起点可以从i-1开始for j in range(i-1, h-z+1):# 如果求和j尚未在上一次投掷中取得,# 则可以直接考虑下一个幸运币if dp[i-1][j] == 0:break# 遍历掷出六种不同点数k的情况,# 当前点数则可以取得j+kfor k in range(1, 7):# 如果当前点数j+k超过了击杀所需点数# 则更新dp[i][h-z]# 为dp[i-1][j]对应的概率乘以(7-k)/6if j + k >= h - z:dp[i][h-z] += (7-k)/6 * dp[i-1][j]break# 如果当前点数j+k尚未超过击杀所需点数# 则其概率由dp[i-1][j]六等分后转移得到else:dp[i][j+k] += 1/6 * dp[i-1][j]# 输出最后一行的最后一个元素# 表示打出第y个幸运币后,造成伤害大于等于h-z点的概率print(dp[y][h-z])

Java

import java.util.Scanner;public class Main {public static void main(String[] args) {int y = 0;            // 掷出幸运币的总个数int z = 0;            // 全部造成的固定伤害int x_temp = 0;       // 时来运转获得的幸运币Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int h = scanner.nextInt();for (int i = 0; i < n; i++) {int t = scanner.nextInt();int x = scanner.nextInt();// 时来运转if (t == 1) {x_temp += x;}// 幸运一掷else {y += x_temp;x_temp = 0;z += x;}}// 如果固定伤害已经大于h,直接输出1if (h - z < 0) {System.out.println("1");}// 否则才需要进行dp过程else {// 初始化dp数组// dp[i][j]表示掷出了i个幸运币时,// 有多大的概率可以取得和为j的结果,即造成和为j的伤害。double[][] dp = new double[y + 1][h - z + 1];dp[0][0] = 1.0;// 考虑每一个幸运币for (int i = 1; i <= y; i++) {// 对于每一个幸运币考虑打出i-1个硬币后的// 每一种求和结果的概率// 注意,由于已经掷出了i-1个幸运币// 那么求和结果至少为i-1,因为每个幸运币点数至少为1点// 因此j遍历时起点可以从i-1开始for (int j = i - 1; j <= h - z; j++) {// 如果求和j尚未在上一次投掷中取得,// 则可以直接考虑下一个幸运币if (dp[i - 1][j] == 0) {break;}// 遍历掷出六种不同点数k的情况,// 当前点数则可以取得j+kfor (int k = 1; k <= 6; k++) {// 如果当前点数j+k超过了击杀所需点数// 则更新dp[i][h-z]// 为dp[i-1][j]对应的概率乘以(7-k)/6if (j + k >= h - z) {dp[i][h - z] += (7 - k) / 6.0 * dp[i - 1][j];break;}// 如果当前点数j+k尚未超过击杀所需点数// 则其概率由dp[i-1][j]六等分后转移得到else {dp[i][j + k] += 1.0 / 6.0 * dp[i - 1][j];}}}}// 输出最后一行的最后一个元素// 表示打出第n个幸运币后,造成伤害大于等于h-z点的概率System.out.println(String.format("%.5f", dp[y][h - z]));}}
}

C++

#include <iostream>
#include <vector>
#include <iomanip>using namespace std;int main() {int y = 0;            // 掷出幸运币的总个数int z = 0;            // 全部造成的固定伤害int x_temp = 0;       // 时来运转获得的幸运币int n, h;cin >> n >> h;for (int i = 0; i < n; i++) {int t, x;cin >> t >> x;// 时来运转if (t == 1) {x_temp += x;}// 幸运一掷else {y += x_temp;x_temp = 0;z += x;}}// 如果固定伤害已经大于h,直接输出1if (h - z < 0) {cout << fixed << setprecision(10) << 1 << endl;}// 否则才需要进行dp过程else {// 初始化dp数组// dp[i][j]表示掷出了i个幸运币时,// 有多大的概率可以取得和为j的结果,即造成和为j的伤害。vector<vector<double>> dp(y + 1, vector<double>(h - z + 1, 0));dp[0][0] = 1.0;// 考虑每一个幸运币for (int i = 1; i <= y; i++) {// 对于每一个幸运币考虑打出i-1个硬币后的// 每一种求和结果的概率// 注意,由于已经掷出了i-1个幸运币// 那么求和结果至少为i-1,因为每个幸运币点数至少为1点// 因此j遍历时起点可以从i-1开始for (int j = i - 1; j <= h - z; j++) {// 如果求和j尚未在上一次投掷中取得,// 则可以直接考虑下一个幸运币if (dp[i - 1][j] == 0) {break;}// 遍历掷出六种不同点数k的情况,// 当前点数则可以取得j+kfor (int k = 1; k <= 6; k++) {// 如果当前点数j+k超过了击杀所需点数// 则更新dp[i][h-z]// 为dp[i-1][j]对应的概率乘以(7-k)/6if (j + k >= h - z) {dp[i][h - z] += (7 - k) / 6.0 * dp[i - 1][j];break;}// 如果当前点数j+k尚未超过击杀所需点数// 则其概率由dp[i-1][j]六等分后转移得到else {dp[i][j + k] += 1.0 / 6.0 * dp[i - 1][j];}}}}// 输出最后一行的最后一个元素// 表示打出第n个幸运币后,造成伤害大于等于h-z点的概率cout << fixed << setprecision(5) << dp[y][h - z] << endl;}return 0;
}

时空复杂度

时间复杂度:O(yh)。其中y为投掷出的幸运币的总数,h为BOSS总血量,dp过程需要进行双重循环。

空间复杂度:O(yh)dp数组所占空间。如果使用滚动dp,空间复杂度可以降低到O(h)
华为OD算法冲刺训练
华为OD算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!

课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化

每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!

30+天陪伴式学习,20+直播课时,300+动画图解视频,200+LeetCode经典题,100+华为OD真题,还有简历修改与模拟面试将为你解锁

可查看链接 OD算法冲刺训练课程表 & OD真题汇总(持续更新)

绿色聊天软件戳 od1336了解更多

这篇关于大厂真题:【DP】米哈游2023秋招-米小游与魔法少女-奇运的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/152979

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

uva 10118 dP

题意: 给4列篮子,每次从某一列开始无放回拿蜡烛放入篮子里,并且篮子最多只能放5支蜡烛,数字代表蜡烛的颜色。 当拿出当前颜色的蜡烛在篮子里存在时,猪脚可以把蜡烛带回家。 问最多拿多少只蜡烛。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cs

uva 10069 DP + 大数加法

代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <cl

uva 10029 HASH + DP

题意: 给一个字典,里面有好多单词。单词可以由增加、删除、变换,变成另一个单词,问能变换的最长单词长度。 解析: HASH+dp 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o