hdu4544湫湫系列故事——消灭兔子 priority queue

本文通过一个C++程序示例介绍了如何使用小顶堆解决特定问题:为一系列具有不同射程和价值的箭矢分配目标,确保每个箭矢都能命中目标且总价值最大。代码中展示了小顶堆的实现方法、箭矢排序策略及主要逻辑。
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int N=100009;
struct node{
    int d,p;
    friend bool operator <(const node &a,const node &b){
        return a.p>b.p;//小顶堆,
    }
}r[N];
bool cmp(node a,node b){
    return a.d>b.d;
}
priority_queue<node> q;
int b[N],n,m;
int main(){
    while(scanf("%d%d",&n,&m)==2){
        while(!q.empty())q.pop();
        for(int i=0;i<n;i++) scanf("%d",b+i);
        for(int i=0;i<m;i++)scanf("%d",&r[i].d);
        for(int i=0;i<m;i++){
            scanf("%d",&r[i].p);
        }
    long long ans=0;
    sort(b,b+n);
    sort(r,r+m,cmp);
    //for(int i=0;i<n;i++)cout<<b[i]<<endl;
    int cnt=0;
    bool flag=true;
    for(int i=n-1;i>=0;i--){
        while(cnt<m&& r[cnt].d>=b[i]){//符合条件的才要
            q.push(r[cnt]);cnt++;
        }
        if(q.empty()){
            flag=false;break;
        }
        ans+=q.top().p;
        q.pop();//每种箭只能使用一次
    }
    if(!flag)cout<<"No"<<endl;
    else cout<<ans<<endl;
    }
    return 0;
}

基于51单片机,实现对直流电机的调速、测速以及正反转控制。项目包含完整的仿真文件、源程序、原理图和PCB设计文件,适合学习和实践51单片机在电机控制方面的应用。 功能特点 调速控制:通过按键调整PWM占空比,实现电机的速度调节。 测速功能:采用霍尔传感器非接触式测速,实时显示电机转速。 正反转控制:通过按键切换电机的正转和反转状态。 LCD显示:使用LCD1602液晶显示屏,显示当前的转速和PWM占空比。 硬件组成 主控制器:STC89C51/52单片机(与AT89S51/52、AT89C51/52通用)。 测速传感器:霍尔传感器,用于非接触式测速。 显示模块:LCD1602液晶显示屏,显示转速和占空比。 电机驱动:采用双H桥电路,控制电机的正反转和调速。 软件设计 编程语言:C语言。 开发环境:Keil uVision。 仿真工具:Proteus。 使用说明 液晶屏显示: 第一行显示电机转速(单位:转/分)。 第二行显示PWM占空比(0~100%)。 按键功能: 1键:加速键,短按占空比加1,长按连续加。 2键:减速键,短按占空比减1,长按连续减。 3键:反转切换键,按下后电机反转。 4键:正转切换键,按下后电机正转。 5键:开始暂停键,按一下开始,再按一下暂停。 注意事项 磁铁和霍尔元件的距离应保持在2mm左右,过近可能会在电机转动时碰到霍尔元件,过远则可能导致霍尔元件无法检测到磁铁。 资源文件 仿真文件:Proteus仿真文件,用于模拟电机控制系统的运行。 源程序:Keil uVision项目文件,包含完整的C语言源代码。 原理图:电路设计原理图,详细展示了各模块的连接方式。 PCB设计:PCB布局文件,可用于实际电路板的制作。
【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开研究,重点进行了系统建模与控制策略的设计与仿真验证。通过引入螺旋桨倾斜机构,该无人机能够实现全向力矢量控制,从而具备更强的姿态调节能力和六自由度全驱动特性,克服传统四旋翼欠驱动限制。研究内容涵盖动力学建模、控制系统设计(如PID、MPC等)、Matlab/Simulink环境下的仿真验证,并可能涉及轨迹跟踪、抗干扰能力及稳定性分析,旨在提升无人机在复杂环境下的机动性与控制精度。; 适合人群:具备一定控制理论基础和Matlab/Simulink仿真能力的研究生、科研人员及从事无人机系统开发的工程师,尤其适合研究先进无人机控制算法的技术人员。; 使用场景及目标:①深入理解全驱动四旋翼无人机的动力学建模方法;②掌握基于Matlab/Simulink的无人机控制系统设计与仿真流程;③复现硕士论文级别的研究成果,为科研项目或学术论文提供技术支持与参考。; 阅读建议:建议结合提供的Matlab代码与Simulink模型进行实践操作,重点关注建模推导过程与控制器参数调优,同时可扩展研究不同控制算法的性能对比,以深化对全驱动系统控制机制的理解。
<think> 我们正在解决HDU1873“看病要排队”问题。根据引用内容,这是一个模拟医院排队系统的题目,涉及优先级队列(优先队列)的应用。 题目要求: - 医院有三个医生(编号1,2,3)。 - 每个病人有优先级(1-10,10最高,1最低)。 - 当医生看病时,从自己的队伍中选择优先级最高的病人。如果优先级相同,则选择先来的病人(即按时间顺序,先到先服务)。 - 操作有两种: IN A B:表示有一个病人,优先级为B,挂到医生A的队列中。 OUT A:表示医生A进行一次诊治,从自己的队列中取出优先级最高的病人并输出其编号(编号为入队时给定的顺序号,从1开始递增)。如果队列为空,则输出"EMPTY"。 解题思路: 根据题目描述,我们需要为每个医生维护一个队列,且这个队列需要能够按照优先级(从高到低)排序,当优先级相同时,按照病人到来的顺序(即编号从小到大,因为编号是递增的)排序(即先来的编号小,所以优先级高的先出队,同优先级时编号小的先出队)。 注意:优先队列通常是使用堆(heap)实现的,在C++中可以使用`priority_queue`容器。由于我们需要自定义排序规则,所以需要重载结构体的比较运算符或者自定义比较函数。 根据引用内容,有两种常见实现方式: 1. 使用优先队列(priority_queue)为每个医生创建一个队列(引用[3]和[5])。 2. 使用数组模拟队列,每次插入后排序(引用[4]),但效率较低。 我们更倾向于使用优先队列,因为这样每次插入和取出的时间复杂度较低(O(log n))。 关键点:排序规则 题目要求:优先级高的先出队,优先级相同则按到来顺序(编号小的先出队)。 在优先队列中,默认是大顶堆(即最大的元素在队首)。但是我们自定义比较规则时要注意: 我们希望优先级高的(即priority值大)排在前面,所以当比较两个病人时: - 如果优先级不同,优先级大的应排在前面(即比较时,我们认为优先级大的“小于”优先级小的?因为优先队列默认是小于号定义大顶堆?实际上在C++中,优先队列默认是vector容器,比较函数默认是less,即大的在前,但实际上是使用小于号比较,如果a<b,则b排在a前面。所以我们需要重新定义比较规则。 引用[3]中的结构体比较运算符重载: bool operator<(const node &A)const{ if(A.rank == rank) return A.id < id; // 当优先级相同时,比较id:这里的意思是,如果A.id小于当前id,则当前节点应该排在A后面?我们需要仔细分析。 return A.rank > rank; // 如果优先级不同,则优先级高的在前。这里的意思是:如果A.rank大于当前rank,则当前节点应该排在后面? } 实际上,在优先队列中,我们希望的是:优先级高的先出队,所以优先级高的应该排在队列的前面(即堆顶)。而优先队列中,我们定义的比较函数实际上是“小于”关系,即默认情况下,较大的元素在队首(因为使用less,比较时是a<b则b在a前面)。所以: 对于两个节点a和b,如果a.rank>b.rank,那么a应该排在b前面(即a<b返回false,因为a不小于b,所以a应该排在b前面?这里需要反过来理解)。 另一种方式(引用[5]): bool operator < (const myNode &o1)const { if(priority_1 == o1.priority_1) return priority_2 > o1.priority_2; // 优先级相同时,编号大的优先级低?但是我们希望编号小的先出队,所以当当前节点的priority_2(编号)大于o1的编号时,我们返回true,表示当前节点小于o1?这样o1就会排到当前节点前面?所以实际上这样定义:编号大的反而优先级低,所以不会排到前面。 else return priority_1 < o1.priority_1; // 如果当前节点的优先级小于o1的优先级,则返回true,表示当前节点小于o1,那么o1就会排到当前节点前面,所以优先级高的(数值大)就会在前面。 } 实际上,在优先队列中,元素按照从大到小排列(队首最大)。所以当我们定义比较运算符时,我们定义的是“小于”关系(即a<b为true时,a排在b的后面)。因此: 对于优先级,我们希望:如果病人的优先级越高,则它应该排在前面(即比较时,它应该“大”)。所以: - 当两个病人的优先级不同时:如果病人a的优先级大于病人b的优先级,那么a应该排在b前面。在比较函数中,如果我们使用小于号定义,则应该让a<b返回false(即a不小于b,那么a就排前面)。或者反过来,我们也可以定义成b>a然后返回,但标准做法是: 如果我们想让a排在b前面(即a>b),那么比较函数应该定义为:当a的优先级大于b的优先级,或者当优先级相同时a的编号小于b的编号(这样a先来),那么a就应该排在b前面。但是优先队列中的比较函数是“小于”函数,所以我们可以这样写: 如果a的优先级小于b的优先级,那么a应该排在b后面(所以返回true?不对) -> 重新理解: 标准做法:我们定义结构体的比较运算符重载(operator<),这个运算符用于决定在优先队列中的位置。优先队列默认是最大堆,即队首元素是最大的。所以我们需要定义:当a的优先级低于b的优先级,或者优先级相同但a的编号大于b的编号时,a应该排在b的后面(即b应该排在a的前面)。那么,在比较函数中,如果a应该排在b后面,那么a < b 应该返回true吗?不对,实际上在优先队列中,比较函数返回true表示第一个参数应该排在第二个参数后面(在堆中下沉)。所以: 我们希望:在队列中,优先级高的(数值大)在前面,同优先级时编号小的在前面(即先来的在前面)。 因此,在比较两个节点a和b时: 如果a的优先级小于b的优先级,那么a应该排在b后面,所以比较函数应该返回true(表示a小于b,所以b应该排在a前面?)但是优先队列的排序规则是:如果比较函数返回true,则第一个参数(a)将排在第二个参数(b)的后面。所以: 当a的优先级小于b的优先级时,我们返回true,这样b就会在a的前面(因为b的优先级高,我们希望b先出队)。 当a的优先级等于b的优先级时,我们比较编号:因为编号小的先来,应该先出队,所以当a的编号大于b的编号(即a后来),那么a应该排在b后面,所以返回true;反之返回false。 但是引用[3]的写法: if(A.rank == rank) return A.id < id; // 这里A是传入的参数,当前节点是this。假设我们比较的是 *this 和 A:当A.id < id(即当前节点的id大于A的id)时,返回true。那么,在优先级相同的情况下,如果当前节点的id大于A的id(即当前节点后到),那么当前节点应该排在A的后面?所以这个比较函数的意思是:如果A的id小于当前id(即A比当前节点先来),那么当前节点应该排在后面(所以返回true表示当前节点“小于”A?不对,因为比较函数是当前节点和A的比较,返回true表示当前节点应该排在A后面)。所以这个写法是正确的。 引用[5]的写法: if(priority_1 == o1.priority_1) return priority_2 > o1.priority_2; // 这里priority_2是当前节点的编号,o1.priority_2是另一个节点的编号。如果当前节点的编号大于o1的编号,返回true。那么,在优先级相同时,如果当前节点编号大于o1的编号(即当前节点后到),那么当前节点应该排在o1后面(即当前节点“小于”o1,所以返回true表示当前节点应该排在后面),这样o1就会排在前面(因为o1编号小,先来)。所以这样写也是正确的。 但是两种写法在比较优先级不同的情况: 引用[3]: return A.rank > rank; // 意思是:如果A.rank大于当前rank,则返回true。那么当A的优先级大于当前节点的优先级时,当前节点应该排在A后面(即返回true表示当前节点“小于”A,所以A应该排在当前节点前面)。正确。 引用[5]: return priority_1 < o1.priority_1; // 当前节点的优先级小于o1的优先级,则返回true。那么当前节点应该排在o1的后面(因为o1的优先级高),正确。 因此,两种写法都可以。 我们采用类似引用[5]的写法,因为更直观: 定义结构体Node,包含两个成员:优先级(prio)和编号(id)。 重载小于运算符(operator<): bool operator < (const Node & other) const { if (prio != other.prio) { return prio < other.prio; // 优先级不同,如果当前节点的优先级小于另一个节点的优先级,则返回true(表示当前节点应该排在另一个节点后面),这样另一个节点(优先级高)就会排在前面。 } else { return id > other.id; // 优先级相同,如果当前节点编号大于另一个节点的编号(即当前节点来得晚),则返回true,表示当前节点应该排在后面,所以另一个节点(来得早)排在前面。 } } 注意:在C++的优先队列中,默认使用less<Node>,即使用Node的<运算符,并且是最大堆(队首元素最大)。我们这里定义的<运算符,当a<b时,表示a应该排在b后面(即b应该在a前面),这样队首的元素就是我们想要的最大优先级(且同优先级时最小编号)的元素。 实现步骤: 1. 定义结构体Patient(或Node),包含优先级(priority)和编号(id),并重载<运算符。 2. 创建三个优先队列(分别对应三个医生),类型为priority_queue<Patient>。 3. 用一个计数器记录当前的编号(每来一个病人,计数器加1)。 4. 读取操作: 如果是"IN":读取医生编号a和优先级b,然后将一个Patient{b, 当前编号}加入医生a对应的队列,并将计数器加1。 如果是"OUT":读取医生编号a,然后判断该医生的队列是否为空: 为空:输出"EMPTY" 不为空:取出队首元素(即top()),输出其编号,然后弹出(pop())。 注意:题目要求多组测试数据。 代码实现(以C++为例): 根据引用[5]的代码稍作修改,并添加注释。 参考代码: ```cpp #include <iostream> #include <queue> #include <string> using namespace std; struct Patient { int priority; // 优先级,数值越大优先级越高 int id; // 病人编号(按到来的顺序递增) // 重载小于运算符 bool operator < (const Patient & other) const { if (priority != other.priority) { return priority < other.priority; // 优先级高的排在前面(当当前优先级小于另一个时,当前排在后面) } else { return id > other.id; // 优先级相同时,编号小的(先来的)排在前面(所以当当前id大于另一个id时,当前排在后面) } } }; int main() { int n; while (cin >> n) { // 多组测试数据 priority_queue<Patient> doctors[4]; // 下标1~3对应三个医生 int patientId = 0; // 病人编号计数器,从1开始 for (int i = 0; i < n; i++) { string op; cin >> op; if (op == "IN") { int doctorIndex, priority; cin >> doctorIndex >> priority; patientId++; Patient patient = {priority, patientId}; doctors[doctorIndex].push(patient); } else if (op == "OUT") { int doctorIndex; cin >> doctorIndex; if (doctors[doctorIndex].empty()) { cout << "EMPTY" << endl; } else { Patient patient = doctors[doctorIndex].top(); cout << patient.id << endl; doctors[doctorIndex].pop(); } } } } return 0; } ``` 注意:医生编号为1,2,3,所以我们创建了长度为4的优先队列数组(下标0不使用)。 这个程序可以满足题目要求。 其他方法(如引用[4]的数组排序)在数据量大时可能效率较低,而优先队列每次操作时间复杂度为O(log n),更优。 总结:本题主要考察优先队列(堆)的应用以及自定义比较规则。 §§相关问题§§ 1. 在HDU1873中,为什么优先队列中重载小于运算符时,优先级相同的情况下要比较病人编号? 2. 如果不使用优先队列,还有什么方法可以解决HDU1873? 3. 优先队列的时间复杂度是多少?在本题中的应用有什么优势? 4. 在C++的优先队列中,自定义比较函数还有哪些方式? 5. 如果医院有多个医生,比如100个医生,上述代码如何扩展? </think> ### HDU1873 看病要排队 解题思路及代码实现 #### 题目分析 题目要求模拟医院三个医生的就诊排队系统,核心规则: 1. **优先级规则**:病情优先级分10级(10最高,1最低) 2. **排队规则**: - 医生选择队列中优先级最高的病人 - 同优先级时选择最早排队的病人(按病人编号顺序) 3. **操作指令**: - `IN A B`:病人挂到医生A的队列,优先级为B - `OUT A`:医生A接诊,输出病人编号(若队列空输出`EMPTY`) #### 解题思路 1. **数据结构选择**: - 为每个医生创建**优先队列**(最大堆) - 队列排序规则: - 第一优先级:病情级别(数值大的优先) - 第二优先级:病人编号(编号小的优先,即先到先服务) 2. **算法设计**: ```mermaid graph TD A[开始] --> B{读取操作} B --> |IN| C[获取医生ID和优先级] C --> D[生成病人编号] D --> E[压入对应医生队列] B --> |OUT| F[获取医生ID] F --> G{队列空?} G --> |是| H[输出'EMPTY'] G --> |否| I[弹出队首元素] I --> J[输出病人编号] ``` #### 代码实现(C++) ```cpp #include <iostream> #include <queue> using namespace std; struct Patient { int priority; // 病情优先级 int id; // 病人编号 // 重载小于运算符(优先队列默认最大堆) bool operator < (const Patient& other) const { if (priority == other.priority) { return id > other.id; // 同优先级时,编号小的优先 } return priority < other.priority; // 优先级高的优先 } }; int main() { int n; while (cin >> n) { priority_queue<Patient> doctors[4]; // 1~3号医生队列 int patientCount = 0; // 全局病人计数器 for (int i = 0; i < n; i++) { string op; cin >> op; if (op == "IN") { int doctorID, priority; cin >> doctorID >> priority; Patient p = {priority, ++patientCount}; doctors[doctorID].push(p); } else if (op == "OUT") { int doctorID; cin >> doctorID; if (doctors[doctorID].empty()) { cout << "EMPTY" << endl; } else { Patient p = doctors[doctorID].top(); cout << p.id << endl; doctors[doctorID].pop(); } } } } return 0; } ``` #### 关键点解析 1. **优先队列排序规则**: - 通过重载`operator<`实现自定义比较:`priority`大的优先,相同则`id`小的优先 - 注意:优先队列默认最大堆,因此实际排序方向与比较符相反[^3][^5] 2. **时间复杂度**: - 插入操作(IN):$O(\log k)$(k为队列长度) - 弹出操作(OUT):$O(\log k)$ - 优于数组排序解法($O(n^2)$)[^4] 3. **空间复杂度**: - $O(M)$,M为病人总数 - 三个队列独立存储病人 #### 扩展优化方向 1. **动态医生数量**:改用`vector<priority_queue>`可支持任意医生数量 2. **错误处理**:增加非法医生ID检测 3. **内存优化**:病人离队后及时释放内存 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值