BZOJ1216:[HNOI2003]操作系统

我对模拟的理解:https://www.cnblogs.com/AKMer/p/9064018.html

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=1216

不兹磁多线程的系统不是好系统>_<

对于每个进程,我们都记录下关于它的五个参数:进程号,起始时间,持续时间,终结时间以及优先级。

按照题意,优先级越大越应优先执行,同优先级的比起始时间,用堆来维护已经进来排队的进程,然后按题意模拟即可。

因为学长说想要(NOI)金牌,堆肯定是要会手写的,所以我一直都用手写堆,而且个人觉得(C++)(stl)除了(vector)以外都可以手动模拟的……

时间复杂度:(O(n log n))

空间复杂度:(O(n))

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=350000;

int n=1;

struct AK {
	int id,st,ed,lst,lv;
}a[maxn];

bool cmp_ed(AK a,AK b) {
	return a.ed<b.ed;
}

bool check(int x,int y) {
	if(a[x].lv==a[y].lv)return a[x].st<a[y].st;
	return a[x].lv>a[y].lv;
}//判断x进程是否比y进程优秀

struct heap {
	int tot;
	int tree[15005];

	void ins(int u) {
		tree[++tot]=u;int now=tot;
		while(now!=1)
			if(check(tree[now],tree[now>>1]))
				swap(tree[now],tree[now>>1]),now>>=1;
			else break;
	}//将一个元素插入堆

	void pop() {
	    tree[1]=tree[tot--];
		int now=1;
		while(now<<1<=tot) {
			int son=now<<1|1;
			if((now<<1|1)>tot||check(tree[now<<1],tree[now<<1|1]))
			    son=now<<1;
			if(check(tree[now],tree[son]))break;
			else swap(tree[now],tree[son]),now=son;
		}
	}//从堆顶弹一个元素出去
}T;

int main() {
	while(~scanf("%d%d%d%d",&a[n].id,&a[n].st,&a[n].lst,&a[n].lv))
		n++;//先把所有进程都读入
	a[n].st=2147483647;//安排一个很靠后时间进来的进程把堆里的进程全部pop出去
	for(int i=1;i<n;i++) {
		T.ins(i);int sum=0;//让i进程开始排队,sum为从i号进程的开始时间起已经用了多少时间
		while(T.tot&&a[i].st+sum+a[T.tree[1]].lst<=a[i+1].st) {//如果可以把当前优先级最高的进程做完
			sum+=a[T.tree[1]].lst;
			a[T.tree[1]].ed=a[i].st+sum;
		    T.pop();
		}
		if(T.tot)a[T.tree[1]].lst-=a[i+1].st-a[i].st-sum;//如果做不完当前优先级最高的进程,那么在下一个进程进来之前能做多少是多少
	}
	sort(a+1,a+n,cmp_ed);//按结束时间排序,输出答案
	for(int i=1;i<n;i++)
		printf("%d %d
",a[i].id,a[i].ed);
	return 0;
}
原文地址:https://www.cnblogs.com/AKMer/p/9097184.html