news 2026/5/15 0:16:13

UVa 226 MIDI Preprocessing

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UVa 226 MIDI Preprocessing

问题分析

MIDI\texttt{MIDI}MIDI(乐器数字接口)是一种用于计算机与合成乐器之间通信的标准。本题中,我们需要处理简单的MIDI\texttt{MIDI}MIDI程序,这些程序由一系列命令组成,每条命令包含时间单位命令类型ONOFF)和音符编号

然而,许多现有音乐无法直接翻译成这种程序形式。主要存在两类问题:

  1. 重复开启问题:当一个音符已经处于ON状态时,再次发送ON命令会被忽略。修复方法是在第二个ON命令之前111个时间单位插入一个OFF命令。

  2. 同时开启和关闭问题:当ONOFF命令出现在同一时间单位时,处理顺序会导致问题。修复方法是将OFF命令移动到对应ON命令之前111个时间单位。

解题思路

本题的核心是按音符编号分组处理命令,因为不同音符的命令互不影响。

数据结构设计

structcommand{inttime_unit;// 时间单位intswitches;// 1 表示 ON,0 表示 OFFintnote_number;// 音符编号boolvalid;// 该命令是否有效};
  • 使用buffer映射(音符编号→\to命令列表)来暂存每个音符的命令序列。
  • 使用counter记录当前处于ON状态的命令数量,用于判断音符是否已开启。

处理流程

  1. 读取输入:按行读取命令,直到遇到-1(程序分隔符)或-2$(程序终止符)。
  2. 分类处理
    • 若命令为OFF,直接加入缓冲区,计数器减111
    • 若命令为ON,需要判断是否应直接加入或触发预处理:
      • 如果当前音符已有未配对的ONcounter>0counter > 0counter>0)或
      • 缓冲区为空或
      • 当前时间与缓冲区最后一个命令时间相同
      • 则直接将命令加入缓冲区,计数器加111
      • 否则,调用preprocess处理当前缓冲区的命令序列,然后清空缓冲区并加入当前命令。
  3. 预处理函数preprocess
    • 将连续的ONOFF命令配对(按输入顺序)。
    • 检查配对后的命令对,如果后一个ON的时间≤\le前一个OFF的时间,则调整前一个OFF的时间为后一个ON的时间减111
    • 如果调整后的OFF时间等于对应 `ON$ 的时间,则标记这两个命令为无效(应被消除)。
    • 将有效的命令收集到全局命令列表中。
  4. 排序输出:对所有有效命令按时间排序,时间相同时ON排在OFF之前(因为题目要求先处理ON再处理OFF,但输出时ON在前),音符编号小的在前。最后输出分隔符-1-2

排序规则

在重载<运算符时:

  • 首先按时间升序排列;
  • 时间相同时,ON111)排在OFF000)之前;
  • 若命令类型相同,则按音符编号升序排列。

代码实现

// MIDI Preprocessing// UVa ID: 226// Verdict: Accepted// Submission Date: 2016-06-02// UVa Run Time: 0.050s//// 版权所有(C)2016,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;constintON=1,OFF=0;// ON 和 OFF 的常量表示// 命令结构体,表示一条 MIDI 指令structcommand{inttime_unit;// 命令执行的时间单位intswitches;// 命令类型:1 = ON, 0 = OFFintnote_number;// 音符编号,范围 1 到 127boolvalid;// 该命令是否有效(预处理后可能被标记为无效)// 排序运算符,用于最终输出排序booloperator<(command x)const{if(time_unit!=x.time_unit)returntime_unit<x.time_unit;// 首先按时间升序elseif(switches!=x.switches)returnswitches>x.switches;// 时间相同,ON 排在 OFF 前面elsereturnnote_number<x.note_number;// 最后按音符编号升序}};vector<command>commands;// 存储所有有效命令map<int,vector<command>>buffer;// 按音符编号分组的临时缓冲区map<int,int>counter;// 记录每个音符当前 ON 的数量// 预处理函数:对某个音符的命令序列进行修复voidpreprocess(vector<command>&program){vector<pair<command,command>>fixed;// 存储配对的 (ON, OFF) 命令对command empty;// 空命令,用作占位符// 将 ON 和 OFF 命令按顺序配对for(inti=0,currentIndex=0;i<program.size();i++){if(program[i].switches==ON)// 遇到 ON 命令,创建新的命令对fixed.push_back(make_pair(program[i],empty));else// 遇到 OFF 命令,填充到当前未完成的命令对中{fixed[currentIndex].second=program[i];currentIndex++;}}// 根据规则修复命令对for(inti=1;i<fixed.size();i++){// 情况1:后一个 ON 的时间 <= 前一个 OFF 的时间// 需要将前一个 OFF 的时间调整到后一个 ON 的时间减 1if(fixed[i].first.time_unit<=fixed[i-1].second.time_unit){fixed[i-1].second.time_unit=fixed[i].first.time_unit;fixed[i-1].second.time_unit--;fixed[i-1].second.switches=OFF;// 确保是 OFF 命令// 保留当前 ON 命令(实际上是保留其类型)fixed[i].first.switches=ON;}// 情况2:调整后的 OFF 时间等于对应 ON 的时间// 这两个命令应被消除,标记为无效if(fixed[i-1].second.time_unit==fixed[i-1].first.time_unit){fixed[i-1].second.valid=false;fixed[i].first.valid=false;}}// 将所有有效的命令收集到全局命令列表中for(inti=0;i<fixed.size();i++){if(fixed[i].first.valid)commands.push_back(fixed[i].first);if(fixed[i].second.valid)commands.push_back(fixed[i].second);}}intmain(intargc,char*argv[]){cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);string line;while(true){// 重置全局变量,准备处理新的 MIDI 程序commands.clear(),buffer.clear(),counter.clear();inttime_unit_in,note_number_in;string switches_in;// 读取一个程序的所有命令,直到遇到 -1 或 -2while(getline(cin,line),line!="-1"&&line!="-2"){command aCommand;istringstreamiss(line);iss>>time_unit_in>>switches_in>>note_number_in;aCommand.time_unit=time_unit_in;aCommand.switches=switches_in=="ON";// ON 为 true (1),OFF 为 false (0)aCommand.note_number=note_number_in;aCommand.valid=true;// 初始时所有命令都有效// 如果命令是 OFF,直接加入缓冲区并减少计数器if(aCommand.switches==OFF){buffer[aCommand.note_number].push_back(aCommand);counter[aCommand.note_number]--;}else// 命令是 ON{// 条件判断:是否需要立即处理当前缓冲区的命令序列if(counter[aCommand.note_number]>0||// 已有未配对的 ONbuffer[aCommand.note_number].size()==0||// 缓冲区为空aCommand.time_unit==buffer[aCommand.note_number].back().time_unit)// 时间与最后命令相同{// 直接加入缓冲区buffer[aCommand.note_number].push_back(aCommand);counter[aCommand.note_number]++;}else{// 处理当前缓冲区的所有命令preprocess(buffer[aCommand.note_number]);// 清空缓冲区,加入当前命令作为新序列的开始buffer[aCommand.note_number].clear();buffer[aCommand.note_number].push_back(aCommand);counter[aCommand.note_number]=1;}}}// 处理剩余的缓冲区命令(每个音符最后未配对的命令序列)for(inti=1;i<=127;i++)if(buffer[i].size()>0)preprocess(buffer[i]);// 对所有有效命令进行排序sort(commands.begin(),commands.end());// 输出修复后的 MIDI 程序for(autoit=commands.begin();it!=commands.end();it++){cout<<(*it).time_unit<<" "<<((*it).switches?"ON":"OFF");cout<<" "<<(*it).note_number<<"\n";}// 输出程序分隔符cout<<line<<"\n";// 遇到 -2 表示所有程序处理完毕if(line=="-2")break;}return0;}

算法复杂度

  • 时间复杂度O(Nlog⁡N)O(N \log N)O(NlogN),其中NNN为命令总数,主要来自最终的排序操作。每个音符的预处理是线性的。
  • 空间复杂度O(N)O(N)O(N),用于存储所有命令。

注意事项

  1. 输入中命令的时间是非递减的,但不同音符的命令可能交错出现,因此需要按音符分组处理。
  2. ONOFF同时出现在同一时间时,根据题目描述,插入OFF的时间为对应ON时间减111,如果结果与前一命令冲突,则需要进一步消除。
  3. 题目保证每个ON都有匹配的OFF,且每个时间、每个音符最多只有一个ON和一个OFF
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 0:16:12

UVa 227 Puzzle

题目分析 本题是一个经典的 555 \times 555 滑动拼图问题。一个 555 \times 555 的框架中包含了 242424 个印有字母的小方块和一个空白位置。可以通过将空白位置上下左右的相邻方块滑入空白处来改变拼图的布局。题目会给出拼图的初始布局以及一系列移动指令&#xff0c;要求输出…

作者头像 李华
网站建设 2026/5/15 0:13:11

终极开源Flash逆向工具:JPEXS Free Flash Decompiler专业实战指南

终极开源Flash逆向工具&#xff1a;JPEXS Free Flash Decompiler专业实战指南 【免费下载链接】jpexs-decompiler JPEXS Free Flash Decompiler 项目地址: https://gitcode.com/gh_mirrors/jp/jpexs-decompiler 你是否面对加密的SWF文件束手无策&#xff1f;想要提取Fla…

作者头像 李华
网站建设 2026/5/15 0:11:02

Unity 2019.4.7f1实战:从零复刻Flappy Bird,搞定PC/Web/安卓多平台发布

Unity 2019.4.7f1实战&#xff1a;从零复刻Flappy Bird&#xff0c;搞定PC/Web/安卓多平台发布 在游戏开发领域&#xff0c;复刻经典小游戏是掌握引擎核心功能的最佳实践方式之一。Flappy Bird以其极简的玩法和令人上瘾的难度曲线&#xff0c;成为无数开发者入门Unity的首选项目…

作者头像 李华
网站建设 2026/5/15 0:09:30

从COCO关键点到YOLO姿态:数据集解析与格式转换实战

1. COCO关键点数据集深度解析 第一次接触COCO数据集时&#xff0c;我被它复杂的标注结构弄得晕头转向。这个被广泛使用的数据集其实藏着不少"彩蛋"&#xff0c;特别是人体关键点部分。COCO的人体姿态标注包含17个关键点&#xff0c;从头顶到脚底覆盖整个人体。每个关…

作者头像 李华
网站建设 2026/5/15 0:09:09

RT-Thread中断处理实战:从机制原理到嵌入式实时系统设计

1. 项目概述与核心价值搞嵌入式开发的朋友&#xff0c;对RT-Thread这个国产的物联网操作系统应该都不陌生。从最开始的点灯、串口打印&#xff0c;到后面玩线程、信号量、邮箱&#xff0c;一路摸索过来&#xff0c;感觉就像在搭积木&#xff0c;一块块地把系统功能给垒起来。但…

作者头像 李华
网站建设 2026/5/15 0:09:06

AI推理模型工程2026:从o3到DeepSeek-R1的工程化落地实践

推理模型&#xff08;Reasoning Model&#xff09;正在重新定义AI应用的边界。当OpenAI o3在ARC-AGI测试上突破人类基准&#xff0c;当DeepSeek-R1以极低成本实现顶级推理能力&#xff0c;工程师们面临的问题已经不是"推理模型能做什么"&#xff0c;而是"怎么把…

作者头像 李华