[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中插入了一些很小的对象,然后释放了一些,然后 ...
随机推荐
- java中如何根据函数查询引用的jar包
选中函数,按Ctrl+Shift+T,就可以弹出对应的jar包地址 例如:
- Spring Boot注入RestTemplate ,出现空指针解决办法
SpringBoot 注入RestTemplate 我看了一下大都是让我们在启动类里面加一个Bean配置代码如下 @Autowired private RestTemplateBuilder buil ...
- 04.重建二叉树 (Java)
题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...
- JVM----Java内存模型与线程
我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户IO等待导致CPU的等 ...
- selenium_java
等待 页面全加载 ChromeOptions options=new ChromeOptions(); /** *(1) NONE: 当html下载完成之后,不等待解析完成,selenium会直接返回 ...
- WOE1-Feature Selection 相关:一个计算WOE和Information Value的python工具
python信用评分卡建模(附代码,博主录制) https://study.163.com/course/introduction.htm?courseId=1005214003&utm_ca ...
- LNMPA是什么?
也许大家对LAMP.LNMP比较熟悉,LAMP代表Linux下Apache.MySQL.PHP这种网站服务器架构:LNMP代表的是Linux下Nginx.MySQL.PHP这种网站服务器架构.LNMP ...
- ubuntu下如何高速下载?
答: 使用uget工具 1.安装uget sudo apt-get install uget -y 2.下载时在设置里指定最大连接数 笔者指定最大连接数为10,可以适当调整此值
- SVN 撤回(回滚)提交的代码
转: SVN 撤回(回滚)提交的代码 2016年12月20日 17:20:58 怀色 阅读数 68614 标签: svnsvn回滚版本回滚 更多 个人分类: svn 版权声明:本文为博主原创文章, ...
- ABAP 判断字符串是否是数字
通过正则表达式: IF cl_abap_matcher=>matches( pattern = '^(-?[1-9]\d*(\.\d*[1-9])?)|(-?0\.\d*[1-9])$' tex ...