[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中插入了一些很小的对象,然后释放了一些,然后 ...
随机推荐
- mac重启php7.0
killall php-fpm /usr/local/opt/php70/sbin/php70-fpm restart
- 使用matplotlib绘制常用图表(3)-其他图表绘制
一.绘制三点图 """ 三月份最高气温 a = [12,15,18,6,7,5,6,8,9,10,15,10,4,5,11,10,5,6,12,15,10,5,14,10 ...
- Codeforces Gym 101630J Travelling from Petersburg to Moscow (最短路)
题目链接 http://codeforces.com/gym/101630/attachments 题解 zyb学长的题. 先枚举第\(k\)大的边权,设其边权为\(x\),然后把每条边边权减掉\(x ...
- VM中的Centos 7配置静态IP
环境: CentOS Linux release 7.6.1810 (Core) VM 网卡配置 将VM的网卡配置成桥接模式 在cmd中查看本机的ip,子网掩码,网关,虚拟机中配置需要和本机在同一个网 ...
- hadoop-httpfs
Hadoop-httpfs: client向httpfs提交文件操作,由httpfs和集群交互: 优势:client不必访问集群 WebHDFS API: https://archive.cloude ...
- 统计mysql某个数据库的表数量以及表记录数
统计MySQL中某个数据库中有多少张表 SELECT count(*) TABLES, table_schema FROM information_schema.TABLES where ...
- springboot+dubbo+zookeeper+mybatis
参考地址:https://www.cnblogs.com/gaopengfirst/p/9555240.html 首先创建一个maven项目: 再在该父项目中创建3个module,分别是:provid ...
- 实验三《敏捷开发与XP实践》_实验报告
实验三<敏捷开发与XP实践>_实验报告 一.实验内容和步骤 提交点1: 任务要求: 实验三 敏捷开发与XP实践 http://www.cnblogs.com/rocedu/p/479577 ...
- https知识汇总
状态码 含义 100 客户端应当继续发送请求.这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝.客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应.服务器必须在 ...
- C++ STL 中 map 容器
C++ STL 中 map 容器 Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它 ...