以游戏编程的角度看待模拟时间的算法题——以PAT甲级1026 Table Tennis为例
对于需要模拟时间的算法题,可以将开始时间作为游戏的开始(如Unity的Start或UE的BeginPlay),每一秒的模拟作为游戏的画面更新(如Unity的Update或UE的Tick),结束时间可作为游戏的结束(如Unity的OnDestroy或UE的EndPlay)。
从这个角度来看,我们可以将PAT甲级1026 Table Tennis当成一个游戏看待,在游戏进行过程中,游戏玩家可能随时会让一位球员来打球。因此可以将可能到来的球员当成玩家输入来处理。
在每个时刻(每一秒),我们读取玩家输入,然后按照指定的游戏玩法(题目的规则)来进行游戏数据的更新。
对于游戏玩法,其要点如下:
- 球员可能是VIP球员也可能是一个普通球员;
- VIP球员唯一享有的特权就是当有空闲的VIP球桌,他可以上VIP球桌打球。(这意味着:如果没有空闲的VIP球桌,VIP球员就是普通球员;如果没有VIP球员在等待打球,那么VIP球桌就是普通球桌。……看似很复杂,其实不影响游戏逻辑。)
因此按照第二点玩法我们可以给出游戏在每一秒的逻辑:
- 获取玩家输入,获得该时刻到来的球员(题目说每秒只会到来一个球员),将其加入球员等待队列,对于VIP球员,将其额外加入VIP球员等待队列。
- 遍历每一个VIP球桌,按照VIP球员到来的顺序让其上球桌打球
- 遍历每一个球桌,按照球员到来的顺序让其上球桌打球
#include<bits/stdc++.h>
using namespace std;const int kOpenTime = 8 * 60 * 60; // 开门时间 8点
const int kCloseTime = 21 * 60 * 60; // 关门时间 21点// 玩家类
struct Player {int arrvTime; // 球员到达时间int playTime; // 球员占用球桌的时间int waitTime; // 球员等待时间bool isVip;// 球员是不是vipint num; // 球员的输出的编号static const int kInvalidNum = 0xffffff;Player(): arrvTime(kOpenTime), playTime(0), waitTime(0), isVip(false), num(kInvalidNum) {}[[nodiscard]] bool isPlayed() const { return num != kInvalidNum; };
};// 球桌类
struct Table {int endTime; // 上一对球员打完球离开该球桌的时间int id;bool isVip;// 是不是vip桌int serveNum; // 桌子招待过多少球员Table(): id{-1}, endTime(kOpenTime), isVip(false), serveNum(0) {}[[nodiscard]] bool isFree(int nowTime) const { return endTime <= nowTime; }
};// 游戏场景中某个对象所挂载的脚本类
class TableTennisManager
{
private:vector<Player> m_players;// 下面两个queue内存储的是m_players内元素的指针queue<Player *> playersWaitQueue;queue<Player *> vipPlayersWaitQueue;int m_unarrivedIDBegin = 0;int m_playedCnt = 0; // 记录球员上桌的顺序vector<Table *> m_tables_VIP;vector<Table *> m_tables;// 将每个时刻到来的球员当做一个输入,将该球员放入队列中让HandleQueue来处理void HandleInput(int nowTime) {for(int playerID = m_unarrivedIDBegin; playerID < m_players.size(); playerID++) {Player & player = m_players[playerID];if(player.arrvTime <= nowTime) {m_unarrivedIDBegin++;playersWaitQueue.emplace(&player);if(player.isVip)vipPlayersWaitQueue.emplace(&player);} else {break;}}}void HandleQueue(int nowTime, vector<Table *> & tables, queue<Player *> & waitPlayers){// 清除已处理过的球员while(!waitPlayers.empty()) {if(waitPlayers.front()->isPlayed()) {waitPlayers.pop();} else {break;}}for(auto table: tables) {if(!table->isFree(nowTime))continue;if(waitPlayers.empty())break;/* 该桌空闲,让正在等待的球员上桌 */// 弹出等待队列,处理该球员Player * player = waitPlayers.front();waitPlayers.pop();// 更新球员数据player->waitTime = nowTime - player->arrvTime;player->num = m_playedCnt++;// 更新球桌数据table->endTime = nowTime + player->playTime;table->serveNum++;}}public:/// 游戏开始,接收游戏过程中可能需要的数据输入,模拟玩家输入并构造游戏场景————在第一次调用Update前调用// m_players模拟玩家输入,m_tables是游戏场景所需的游戏对象void Begin(){int nPlayers, nTables, nVipTables; // 球员对的数量、球桌数、VIP球桌数// 输入在营业时间内到达的球员数据cin >> nPlayers;m_players.resize(nPlayers);int nPlayers_real = 0; // 记录符合要求的输入数据(到达时间在21点之前的球员)for(int i = 0; i < nPlayers; ++i) {// 输入:每队球员的到达时间、占用球桌的时间(最多不能超过两小时)、是否是VIPint h, m, s, playT, isVip;scanf("%d:%d:%d %d %d\n", &h, &m, &s, &playT, &isVip); //NOLINTint arvT = (h*60 + m)*60 + s; // 按照秒数记录player到达时间// 只保留在关门前到达的球员if(arvT < kCloseTime){Player player;player.arrvTime = arvT;player.playTime = playT > 120 ? 120 * 60 : playT * 60; // 打球超过2小时的都要强制变成120分钟player.isVip = isVip;m_players[nPlayers_real++] = player;}}m_players.resize(nPlayers_real);// 输入球桌数据cin >> nTables >> nVipTables;m_tables.resize(nTables);for(int i = 0; i < nTables ; i++) {m_tables[i] = new Table{};}// 输入VIP球桌数据for(int i = 0; i < nVipTables; ++i) {int tableID;cin >> tableID;tableID--;m_tables[tableID]->isVip = true; // 标记vip桌m_tables[tableID]->id = tableID;m_tables_VIP.push_back(m_tables[tableID]);}// 按照球员到达时间排序sort(m_players.begin(), m_players.end(), [](Player &a, Player &b){return a.arrvTime < b.arrvTime;});}/// 更新游戏数据,更新游戏场景数据(m_players和m_tables)————每个时刻(每秒)调用一次void Update(int nowTime){// 处理该时刻可能到达的球员HandleInput(nowTime);// 在VIP球桌上处理VIP球员HandleQueue(nowTime, m_tables_VIP, vipPlayersWaitQueue);// 在所有球桌上处理所有球员(上一步因为VIP球桌已满而未能进入VIP球桌的VIP球员将会被视为普通球员处理)HandleQueue(nowTime, m_tables, playersWaitQueue);}/// 游戏结束,输出游戏执行结果————在最后一次Update执行完调用void End(){// 将球员按照打球的顺序进行排序sort(m_players.begin(), m_players.end(), [](Player &a, Player &b){return a.num < b.num;});// 输出球员的打球信息for(Player &player: m_players) {if(player.num == 0xffffff) continue; // 超过21点才能打球的球员就不能输出了int ah = player.arrvTime/3600, am = player.arrvTime % 3600 / 60, as = player.arrvTime % 60;int bh = (player.arrvTime + player.waitTime) / 3600, bm = (player.arrvTime + player.waitTime) % 3600 / 60, bs = (player.arrvTime + player.waitTime) % 60;// 等待的分钟数要四舍五入player.waitTime = player.waitTime/60 + (player.waitTime%60 >= 30);printf("%02d:%02d:%02d %02d:%02d:%02d %d\n",ah,am,as,bh,bm,bs,player.waitTime);}// 输出球桌的接待信息for(int i = 0; i < m_tables.size(); ++i) {cout << m_tables[i]->serveNum << (i == m_tables.size() - 1 ? "\n" : " ");delete m_tables[i];}}
};int main() {TableTennisManager tennisManager;tennisManager.Begin();for (int nowTime = kOpenTime; nowTime < kCloseTime; nowTime++) {// 每秒调用一次,对相关数据进行更新tennisManager.Update(nowTime);}tennisManager.End();return 0;
}相关文章:
以游戏编程的角度看待模拟时间的算法题——以PAT甲级1026 Table Tennis为例
对于需要模拟时间的算法题,可以将开始时间作为游戏的开始(如Unity的Start或UE的BeginPlay),每一秒的模拟作为游戏的画面更新(如Unity的Update或UE的Tick),结束时间可作为游戏的结束(…...
SNAT与DNAT原理
SNAT和DNAT (源地址转换和目标地址转换) SNAT:源地址转换。内网到外网转换的是源地址。 DNAT:目标地址转换:外网到内网转换的是目的地址 (把内部服务器的ip地址转换成一个所有人都可以访问的地址࿰…...
04-2_Qt 5.9 C++开发指南_SpinBox使用
文章目录 1. SpinBox简介2. SpinBox使用2.1 可视化UI设计2.2 widget.h2.3 widget.cpp 1. SpinBox简介 QSpinBox 用于整数的显示和输入,一般显示十进制数,也可以显示二进制、十六进制的数,而且可以在显示框中增加前缀或后缀。 QDoubleSpinBox…...
接口安全防护方案
文章目录 1.认证与授权机制2.参数校验3.接口加密4.防止暴力破解5.安全头设置6.日志监控 1.认证与授权机制 使用令牌(Token)、OAuth等认证方式,确保只有合法用户可以访问接口。授权机制可以防止未经授权的用户访问敏感接口。 示例:…...
机器学习复习题
1 单选题 ID3算法、C4.5算法、CART算法都是( )研究方向的算法。 A . 决策树 B. 随机森林 C. 人工神经网络 D. 贝叶斯学习 参考答案:A ( )作为机器学习重要算法之一,是一种利用多个树分类器进行分类和预测…...
无线液位传感器—简介
近年来,随着无线传感网络技术的愈发成熟和稳定,无线传感器因其安装、维护方便,不用布线、节约成本,监测方便,使用灵活,可适用于多种工业领域等优点,正在逐步替代部分传统有线传感器,…...
通讯协议034——全网独有的OPC HDA知识一之聚合(三)时间加权平均
本文简单介绍OPC HDA规范的基本概念,更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化,以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…...
Android 13 Hotseat定制化修改——003 hotseat图标大小修改
目录 一.背景 二.未修改前效果 三.修改后效果 一.背景 由于需求是需要自定义修改Hotseat,所以此篇文章是记录如何自定义修改hotseat的,应该可以覆盖大部分场景,修改点有修改hotseat布局方向,hotseat图标数量,hotseat图标大小,hotseat布局位置,hotseat图标禁止形成文件…...
21、springboot的宽松绑定及属性处理类的构造注入
springboot的宽松绑定及属性处理类的构造注入 ★ 如何使用属性处理类所读取的属性 属性处理类最终变成了Spring容器中的一个Bean组件,因此接下来Spring即可将该Bean组件注入任意其他组件。 这种做法的好处是:可以将大量的配置信息封装一个对象——所以…...
nginx负载均衡(反向代理)
nginx负载均衡 负载均衡:由反向代理来实现。 nginx的七层代理和四层代理: 七层是最常用的反向代理方式,只能配置在nginx配置文件的http模块当中,而且配置方法名称:upstream模块,不能写在server模块中&#…...
AWS上传私有windows server2019镜像64位
一.制作自己的镜像 我使用的是esxi,建立一个windows虚拟机,开启。 根据aws官方文档,虚拟机里的系统重要需要注意以下几点: 1.只有一张网卡,ip获取配置成dhcp。 2.关闭系统防火墙。 3.开启windows rdp 远程功能。 …...
查看当前仓库对应的远程仓库地址
查看当前仓库对应的远程仓库地址 git remote -v这条命令能显示你当前仓库中已经添加了的仓库名和对应的仓库地址,通常来讲,会有两条一模一样的记录,分别是fetch和push,其中fetch是用来从远程同步 push是用来推送到远程 修改仓库…...
flask-script
# django中,有命令 python manage.py runserver python manage.py makemigrations ...自定制命令(django如何自定制命令)... -python manage.py init_db excel文件路径 指定表名 # flask启动项目,像djag…...
标准的OSI七层模型(其实了解tcp足矣)
七层模型,亦称OSI(Open System Interconnection)。参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系,一般称为OSI参考模型或七层模型。 它是一个七层的、抽象的模型体&#x…...
【C++】初识模板
C模板入门 一、泛型编程 二、函数模板1. 函数模板的概念2. 函数模板格式3. 函数模板的原理4. 函数模板的实例化5. 模板参数的匹配原则 三、类模板 一、泛型编程 假设我们想实现一个交换函数,并且支持不同类型的参数实现,我们可以用 typedef 将类型进行重…...
学习Pull request
我从我的导师Xing Fan指导和帮助,利用我的导师chunlong Li提供ChatGPT,在百度搜索,学习一些资料。以下很多内容都是我的导师Xing Fan做的。谢谢Xing Fan。考虑到隐私,不适合截图公开。 第一步: 打开Git Bash Here 如…...
python爬虫实战(1)--爬取新闻数据
想要每天看到新闻数据又不想占用太多时间去整理,萌生自己抓取新闻网站的想法。 1. 准备工作 使用python语言可以快速实现,调用BeautifulSoup包里面的方法 安装BeautifulSoup pip install BeautifulSoup完成以后引入项目 2. 开发 定义请求头…...
React Hooks 详细使用介绍
useState 状态管理 useState 是 React 中的一个基础 Hook,允许你在不使用 class 组件的情况下管理组件状态。 参数 初始值 你可以直接传递状态的初始值给 useState: const [name, setName] useState("John");使用函数设置初始值 当初始…...
python版《羊了个羊》游戏开发第一天
Python小型项目实战教学课《羊了个羊》 一、项目开发大纲(初级) 版本1.0:基本开发 课次 内容 技术 第一天 基本游戏地图数据 面向过程 第二天 鼠标点击和移动 面向对象 第三天 消除 设计模式:单例模式 第四天 完整…...
【uniapp】原生子窗体subNvue的使用与踩坑
需求 最近接到个需求, 需要在video组件上弹出弹窗, 也就是覆盖video这个原生组件 未播放时, 弹窗可以覆盖, 但是当video播放时, 写的弹窗就覆盖不了了 因为video是原生组件, 层级非常高, 普通标签是覆盖不了的, map标签同理 覆盖原生组件, 官方给出解决办法一. 使用cover-view…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
