P2278 [HNOI2003] 操作系统

[HNOI2003] 操作系统

题目描述

写一个程序来模拟操作系统的进程调度。假设该系统只有一个 CPU,每一个进程的到达时间,执行时间和运行优先级都是已知的。其中运行优先级用自然数表示,数字越大,则优先级越高。

如果一个进程到达的时候 CPU 是空闲的,则它会一直占用 CPU 直到该进程结束。除非在这个过程中,有一个比它优先级高的进程要运行。在这种情况下,这个新的(优先级更高的)进程会占用 CPU,而老的只有等待。

如果一个进程到达时,CPU 正在处理一个比它优先级高或优先级相同的进程,则这个(新到达的)进程必须等待。

一旦 CPU 空闲,如果此时有进程在等待,则选择优先级最高的先运行。如果有多个优先级最高的进程,则选择到达时间最早的。

输入格式

输入包含若干行,每一行有四个自然数(均不超过 1 0 8 10^8 108),分别是进程号,到达时间,执行时间和优先级。不同进程有不同的编号,不会有两个相同优先级的进程同时到达。输入数据已经按到达时间从小到大排序。输入数据保证在任何时候,等待队列中的进程不超过 15000 15000 15000 个。

输出格式

按照进程结束的时间输出每个进程的进程号和结束时间。

样例 #1

样例输入 #1

1 1 5 3 
2 10 5 1 
3 12 7 2 
4 20 2 3 
5 21 9 4 
6 22 2 4 
7 23 5 2 
8 24 2 4

样例输出 #1

1 6
3 19
5 30
6 32
8 34
4 35
7 40
2 42

题目纯模拟,就是很麻烦
这道题看起来是提高+/省选-实际上一点也不难,主要涉及的难点是优先队列重载运算符


优先队列:

首先优先队列虽然叫队列,但是本质上是用堆来维护的。
优先队列与普通队列不同,它的堆头是队列中最大(最小)元素。

下面是优先队列的基本用法
1.priority_queue<类型> q;//定义
2.q.push(元素);//入队,时间复杂度为logn
3.q.pop();//出队,出队头
4.q.top();//获取堆顶元素
5.q.size();//获取元素个数
6.q.empty();//判空,空返回1

这些就是常用的操作,当然优先队列默认是大根堆,所以如果想要让堆顶变为最小的有两种方法,我比较推荐第二种,更加简单,就是在入队时把入队元素变成负数,然后出队时在变回来(虽然简单,但是用处仅限于数字 )
如果需要小根堆的写法,可以在网上搜,我先写一个int类型的。

priority_queue<int,vector<int>,greater<int>> a;

注意:这里本蒟蒻用的c++版本较高,如果低版本请将两个或多个连续的大于号或者小于号用空格隔开,不然会报错(会把两个小于号/大于号当作位运算)

好的,讲完了优先队列似乎就可以做了,但是你会发现这东西需要综合考虑,所以得用结构体。
然后你把它套进去就直接报错了。
原因很简单,优先队列不知道你按啥规则

重载运算符

所以就要用到重载运算符了

struct pr{//这是我写的结构体
	int id,time1,time2,lv;
	bool operator<(const pr& tmp) const{//重载运算符
		if(lv!=tmp.lv){
			return lv<tmp.lv;
		}
		return time1>tmp.time1;
	}
};

重载运算符作用简单,但是用处很大,现在,两个结构体变量就可以直接使用 < < <了,比如这样一段代码:

pr a,b;
a.id=1;
a.time1=10;
a.time2=10;
a.lv=7;
b.id=2;
b.time1=14;
b.time2=10;
b.lv=4;
cout<<b<a;

可以正常输出。

思路

模拟每个进程进入,进入之后先把它进入之前的所有可以运行的进程全部运行(优先队列里的),然后把它放进优先队列就可以了。
最后记得清空队列。

代码

注意:优先队列需要重载的是小于号
这样就能给出完整代码了:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+10;
const int N3=1e3+10;
struct pr{//结构体
	int id,time1,time2,lv;//分别为编号、开始时间、需要时间、优先级
	bool operator<(const pr& tmp) const{//重载运算符
		if(lv!=tmp.lv){
			return lv<tmp.lv;
		}
		return time1>tmp.time1;
	}
};
priority_queue<pr> q;//优先队列
int a,b,c,d;
ll now_time=1;
int main(){
	while(cin>>a>>b>>c>>d){//多组输入,用while(cin>>)
		while(!q.empty()&&now_time+q.top().time2<=b){//当前这个程序到来之前,把可以执行的全都执行了。
			now_time+=q.top().time2;
			printf("%d %lld\n",q.top().id,now_time);
			q.pop();
		}
		if(!q.empty()){//处理执行一半被切断的情况。
			pr t=q.top();
			q.pop();
			t.time2-=b-now_time;
			q.push(t);
		}
		now_time=b;//更新时间
		q.push({a,b,c,d});
	}
	while(!q.empty()){//这里不输出会有问题。
		now_time+=q.top().time2;
		printf("%d %lld\n",q.top().id,now_time);
		q.pop();
	}
	return 0;
}

我后面会单独出一片关于重载运算符的讲解,所以这里就不多讲了。

最近在备考csp-j,更的频率会低。

根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值