[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)的更多相关文章

  1. [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)< ...

  2. C++ STL vector 内存分配

    vector为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储. 当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间.拷贝元素.撤销 ...

  3. STL六大组件之——分配器(内存分配,好深奥的东西)

    SGI设计了双层级配置器,第一级配置器直接使用malloc()和free(),第二级配置器则视情况采用不同的策略:当配置区块超过128bytes时,视之为“足够大”,便调用第一级配置器:当配置区小于1 ...

  4. STL容器的内存分配

    这篇文章参考的是侯捷的<STL源码剖析>,所以主要介绍的是SGI STL实现版本,这个版本也是g++自带的版本,另外有J.Plauger实现版本对应的是cl自带的版本,他们都是基于HP实现 ...

  5. STL内存分配

    STL内存创建 Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu  转载请标明来源 1.      Stl内存创建基类模板__malloc_alloc_tem ...

  6. STL源代码剖析(一) - 内存分配

    Allocaor allocator 指的是空间配置器,用于分配内存.STL中默认使用SGI STL alloc作为STL的内存分配器,尽管未能符合标准规格,但效率上更好.SGI STL也定义有一个符 ...

  7. 解析STL中典型的内存分配

    1 vector 在C++中使用vector应该是非常频繁的,但是你是否知道vector在计算内存分配是如何么? 在c++中vector是非常类似数组,但是他比数组更加灵活,这就表现在他的大小是可以自 ...

  8. (转)C++ STL中的vector的内存分配与释放

    C++ STL中的vector的内存分配与释放http://www.cnblogs.com/biyeymyhjob/archive/2012/09/12/2674004.html 1.vector的内 ...

  9. STL内存分配方式

    关于STL用的很多比如map, vector, set, queue, stack等等.很少关注它的内存分配情况,但是经常遇到比如使用map,不停的在map中插入了一些很小的对象,然后释放了一些,然后 ...

随机推荐

  1. Centos7下设置MySql自动启动

    原文链接:http://www.cnblogs.com/Sungeek/p/9687565.html 1.将服务文件拷贝到init.d下,并重命名为mysql cp /usr/local/mysql/ ...

  2. 2016 CCPC 长春站现场赛总结(流水账= =)

    总的来说在写这篇总结的时候心情还是愉悦的,因为第一次参加区域赛就越过铜直接拿了个银~开心之情无法用语言形容啊233= =... 从杭州坐火车到长春,去的时候24个小时,回来32个小时,在路上就花了2天 ...

  3. truncate at 255 characters with xlsx files(OLEDB方式读取Excel丢失数据、字符串截断的原因和解决方法)

    The TypeGuessRows setting is supported by ACE. Note the version numbers in the key may change depend ...

  4. python文件夹中pycache文件是什么

    python(pycache文件的问题):python属于脚本语言,执行python文件需要通过python解释器将源码转换为字节码,然后供cpu读取,pycache文件夹里面保存的就是py文件对应的 ...

  5. redis如何清空当前缓存和所有缓存

    Windows环境下使用命令行进行redis缓存清理1.redis安装目录下输入cmd2.redis-cli -p 端口号3.flushdb    清除当前数据库缓存4.flushall     清除 ...

  6. CentOS7 磁盘管理

    一.磁盘查看 查看所有磁盘 ll /dev/sd* 不带数字的为磁盘,带数字的为磁盘的分区 查看所有磁盘的分区情况 fdisk -l 结果 WARNING: fdisk GPT support is ...

  7. grep命令和tail命令

    写在前面的话: 最近参与了新项目开发,周期短,与自己负责的主要业务对接.业务复杂,时常出现bug,然额对于菜鸟的我,更是无从下手.其实最好的帮助就是 学会查看日志,关键是之前查看日志真是太少了,菜鸟一 ...

  8. 大牛们是如何开发 WordPress 主题的?

    自己算是写过一个主题,目前也在用( 这里 -> http://udonmai.com/ ),所以多少想说两句. 当初走上web开发的路之后最想干的事情就是写个自己的WP主题...所以网上搜罗了很 ...

  9. 开源缓存框架之ASimpleCache

    ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架.轻量到只有一个java文件(由十几个类精简而来). 1.它可以缓存什么东西? 普通的字符串.JsonObject.Jso ...

  10. mysql 安装 和 mysql 远程连接

    一.mysql安装 1.下载MySQL数据库可以访问官方网站:https://www.mysql.com/ 2.点击DOWNLOADS模块下的Community模块下的MySQL Community ...