[BZOJ 3117] [NOI1999]内存分配(STL)
[BZOJ 3117] [NOI1999]内存分配(STL)
题面
内存是计算机重要的资源之一,程序运行的过程中必须对内存进行分配。 经典的内存分配过程是这样进行的:
1.内存以内存单元为基本单位,每个内存单元用一个固定的整数作为标识,称为地址。地址从0开始连续排列,地址相邻的内存单元被认为是逻辑上连续的。我们把从地址i开始的s个连续的内存单元称为首地址为i长度为s的地址片。
2.运行过程中有若干进程需要占用内存,对于每个进程有一个申请时刻T,需要内存单元数M及运行时间P。在运行时间P内(即T时刻开始,T+P时刻结束),这M个被占用的内存单元不能再被其他进程使用。
3.假设在T时刻有一个进程申请M个单元,且运行时间为P,则:
- 若T时刻内存中存在长度为M的空闲地址片,则系统将这M个空闲单元分配给该进程。若存在多个长度为M个空闲地址片,则系统将首地址最小的那个空闲地址片分配给该进程。
- 如果T时刻不存在长度为M的空闲地址片,则该进程被放入一个等待队列。对于处于等待队列队头的进程,只要在任一时刻,存在长度为M的空闲地址片,系统马上将该进程取出队列,并为它分配内存单元。注意,在进行内存分配处理过程中,处于等待队列队头的进程的处理优先级最高,队列中的其它进程不能先于队头进程被处理。
现在给出一系列描述进程的数据,请编写一程序模拟系统分配内存的过程。
分析
用一个set
维护内存,每个元素是一个区间,表示连续的空闲内存。分配一段内存的时候需要修改区间端点。而释放内存的时候除了要将一个新的区间插入之外,如果和左边和右边的区间是连续的,还需要合并。因为插入之前区间一定不连续,所以只需要合并2次,复杂度\(O(\log n)\)
typedef set< pair<int,int> >sp;
typedef sp::iterator spi;
sp mem;
void new_mem(spi it,int sz){
int l=it->first,r=it->second;
mem.erase(it);
if(l+sz<=r){
mem.insert(make_pair(l+sz,r));
}
}
void free_mem(pair<int,int>x) {
if(mem.empty()){
mem.insert(x);
return;
}
spi pre=mem.lower_bound(x);
if(pre!=mem.begin()) pre--;
if(pre->second+1==x.first){
int newl=pre->first;
mem.erase(pre);
pre=mem.insert(make_pair(newl,x.second)).first;
//insert返回插入的位置
//利用insert的返回值,找到合并后的区间,方便第二次合并
}else pre=mem.insert(x).first;
spi nex=mem.upper_bound(x);
if(nex!=mem.end()&&x.second+1==nex->first){
int newl=pre->first;
int newr=nex->second;
mem.erase(pre);
mem.erase(nex);
mem.insert(make_pair(newl,newr));
}
}
查找空闲内存的时候暴力遍历整个set,复杂度O(能过)。
用priority_queue
维护的当前正在运行的进程,按照结束时间从小到大排序。
struct task {
ll st;//开始时间
ll ed;//结束时间
int space;//内存大小
pair<int,int>p;//占用的内存区间
task() {
}
task(ll _st,ll _ed,int _space,pair<int,int>_p) {
st=_st;
ed=_ed;
space=_space;
p=_p;
}
friend bool operator < (task p,task q) {
return p.ed>q.ed;
}
};
priority_queue<task>now_run;
等待队列直接用一个queue
维护就好了。
每当一个进程请求内存的时候。先把正在运行的进程中,结束时间<请求时间的内存释放。等同一时刻结束的进程结束后,再处理等待队列。然后尝试把当前进程添加,如果空闲内存足够,则直接分配内存。否则加入等待队列。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<utility>
#define maxm 10000
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
int n;
int cnt;
struct oper {
ll t;
int m;
ll p;
} a[maxm+5];
typedef set< pair<int,int> >sp;
typedef sp::iterator spi;
sp mem;
void new_mem(spi it,int sz){
int l=it->first,r=it->second;
mem.erase(it);
if(l+sz<=r){
mem.insert(make_pair(l+sz,r));
}
}
void free_mem(pair<int,int>x) {
if(mem.empty()){
mem.insert(x);
return;
}
spi pre=mem.lower_bound(x);
if(pre!=mem.begin()) pre--;
if(pre->second+1==x.first){
int newl=pre->first;
mem.erase(pre);
pre=mem.insert(make_pair(newl,x.second)).first;
//insert返回插入的位置
//利用insert的返回值,找到合并后的区间,方便第二次合并
}else pre=mem.insert(x).first;
spi nex=mem.upper_bound(x);
if(nex!=mem.end()&&x.second+1==nex->first){
int newl=pre->first;
int newr=nex->second;
mem.erase(pre);
mem.erase(nex);
mem.insert(make_pair(newl,newr));
}
}
pair<int,int> find_mem(int sz){
for(spi it=mem.begin();it!=mem.end();it++){
int l=it->first,r=it->second;
if(r-l+1>=sz){
new_mem(it,sz);
return make_pair(l,l+sz-1);
}
}
return make_pair(-1,-1);
}
void print_mem(){
for(spi it=mem.begin();it!=mem.end();it++){
int l=it->first,r=it->second;
printf("[%d,%d] ",l,r);
}
printf("\n");
}
struct task {
ll st;
ll ed;
int space;
pair<int,int>p;
task() {
}
task(ll _st,ll _ed,int _space,pair<int,int>_p) {
st=_st;
ed=_ed;
space=_space;
p=_p;
}
friend bool operator < (task p,task q) {
return p.ed>q.ed;
}
};
priority_queue<task>now_run;
queue<int>now_wait;
void pop_wait(ll tim) {
while(!now_wait.empty()) {
int id=now_wait.front();
pair<int,int>tmp=find_mem(a[id].m);
if(tmp.first!=-1) {
now_run.push(task(tim,tim+a[id].p,a[id].m,tmp));
now_wait.pop();
} else break;
}
}
ll last=0;
void free_task(ll tim) {
while(!now_run.empty()&&now_run.top().ed<=tim) {
task tmp=now_run.top();
free_mem(tmp.p);
last=max(last,tmp.ed);
now_run.pop();
if(now_run.empty()||now_run.top().ed!=tmp.ed) pop_wait(tmp.ed);//等同一时刻结束的进程释放后,再处理等待的
}
}
int inq_cnt=0;
void add_task(int id) {
free_task(a[id].t);
pair<int,int>tmp=find_mem(a[id].m);
if(tmp.first!=-1) {
now_run.push(task(a[id].t,a[id].t+a[id].p,a[id].m,tmp));
} else {
inq_cnt++;
now_wait.push(id);
}
}
int main() {
int u,v,w;
scanf("%d",&n);
while(scanf("%d %d %d",&u,&v,&w)!=EOF) {
if(u==0&&v==0&&w==0) break;
cnt++;
a[cnt].t=u;
a[cnt].m=v;
a[cnt].p=w;
}
mem.insert(make_pair(0,n-1));
for(int i=1; i<=cnt; i++) {
add_task(i);
}
free_task(INF);
printf("%lld\n%d\n",last,inq_cnt);
}
[BZOJ 3117] [NOI1999]内存分配(STL)的更多相关文章
- [POJ1193][NOI1999]内存分配(链表+模拟)
题意 时 刻 T 内存占用情况 进程事件 0 1 2 3 4 5 6 7 8 9 进程A申请空间(M=3, P=10)<成功> 1 A 2 A B 进程B申请空间(M=4, P=3)< ...
- C++ STL vector 内存分配
vector为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储. 当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间.拷贝元素.撤销 ...
- STL六大组件之——分配器(内存分配,好深奥的东西)
SGI设计了双层级配置器,第一级配置器直接使用malloc()和free(),第二级配置器则视情况采用不同的策略:当配置区块超过128bytes时,视之为“足够大”,便调用第一级配置器:当配置区小于1 ...
- STL容器的内存分配
这篇文章参考的是侯捷的<STL源码剖析>,所以主要介绍的是SGI STL实现版本,这个版本也是g++自带的版本,另外有J.Plauger实现版本对应的是cl自带的版本,他们都是基于HP实现 ...
- STL内存分配
STL内存创建 Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源 1. Stl内存创建基类模板__malloc_alloc_tem ...
- STL源代码剖析(一) - 内存分配
Allocaor allocator 指的是空间配置器,用于分配内存.STL中默认使用SGI STL alloc作为STL的内存分配器,尽管未能符合标准规格,但效率上更好.SGI STL也定义有一个符 ...
- 解析STL中典型的内存分配
1 vector 在C++中使用vector应该是非常频繁的,但是你是否知道vector在计算内存分配是如何么? 在c++中vector是非常类似数组,但是他比数组更加灵活,这就表现在他的大小是可以自 ...
- (转)C++ STL中的vector的内存分配与释放
C++ STL中的vector的内存分配与释放http://www.cnblogs.com/biyeymyhjob/archive/2012/09/12/2674004.html 1.vector的内 ...
- STL内存分配方式
关于STL用的很多比如map, vector, set, queue, stack等等.很少关注它的内存分配情况,但是经常遇到比如使用map,不停的在map中插入了一些很小的对象,然后释放了一些,然后 ...
随机推荐
- 微信小程序_(校园视)开发上传视频业务
微信小程序_(校园视) 开发用户注册登陆 传送门 微信小程序_(校园视) 开发上传视频业务 传送门 微信小程序_(校园视) 开发视频的展示页-上 传送门 微信小程序_(校园视) 开发视频的展示页-下 ...
- JavaWeb-SpringBoot(抖音)_三、抖音项目后续
JavaWeb-SpringBoot(抖音)_一.抖音项目制作 传送门 JavaWeb-SpringBoot(抖音)_二.服务器间通讯 传送门 JavaWeb-SpringBoot(抖音)_三.抖音项 ...
- eureka 服务实例实现快速下线快速感知快速刷新配置解析
Spirng Eureka 默认配置解读 默认的Spring Eureka服务器,服务提供者和服务调用者配置不够灵敏,总是服务提供者在停掉很久之后,服务调用者很长时间并没有感知到变化.或者是服务已经注 ...
- webpack搭建多页面系统(一):对webpack 构建工具的理解
为什么使用webpack构建工具? 1.开发效率方面: 在一般的开发过程中,分发好任务后,每个人完成自己单独的页面,如果有的人开发完成之后,接手别人的任务,就有可能造成开发时候的冲突. 如果利用模块化 ...
- git commit 合并到指定分支
1. 将指定的commit合并到当前分支 git cherry-pick commit_id 2. 合并多个连续 commit 到指定分支 假设需要合并 devlop 上从 fb407a3f 到 9 ...
- PHP 练习:租房子
<form action="text.php" method="post"> 区域:<input type="checkbox&qu ...
- python 面对对象 类(继承, 多态)
继承,继承其它实例化样本的属性和方法,需要在声明里重新定义和使用 class School(object): def __init__(self, name, addr): self.name = n ...
- 在oracle中使用基表建立月表的存储过程
某些系统需要按月分表来保存数据.下面的存储过程演示了如何使用基表来建立每个月的月表. 处理思路是: 1:首先,为基表建立好表和对应的索引. 2:将基表保存到一个存储过程需要的表中. ...
- vue文件流转换成pdf预览(pdf.js+iframe)
参考文档:https://www.jianshu.com/p/242525315bf6 PDFJS: https://mozilla.github.io/pdf.js/ 支持获取文件流到客户端 ...
- 【SQL】MySQL---using的用法
学习记录: mysql中using的用法为: using()用于两张表的join查询,要求using()指定的列在两个表中均存在,并使用之用于join的条件