[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)\)

  1. typedef set< pair<int,int> >sp;
  2. typedef sp::iterator spi;
  3. sp mem;
  4. void new_mem(spi it,int sz){
  5. int l=it->first,r=it->second;
  6. mem.erase(it);
  7. if(l+sz<=r){
  8. mem.insert(make_pair(l+sz,r));
  9. }
  10. }
  11. void free_mem(pair<int,int>x) {
  12. if(mem.empty()){
  13. mem.insert(x);
  14. return;
  15. }
  16. spi pre=mem.lower_bound(x);
  17. if(pre!=mem.begin()) pre--;
  18. if(pre->second+1==x.first){
  19. int newl=pre->first;
  20. mem.erase(pre);
  21. pre=mem.insert(make_pair(newl,x.second)).first;
  22. //insert返回插入的位置
  23. //利用insert的返回值,找到合并后的区间,方便第二次合并
  24. }else pre=mem.insert(x).first;
  25. spi nex=mem.upper_bound(x);
  26. if(nex!=mem.end()&&x.second+1==nex->first){
  27. int newl=pre->first;
  28. int newr=nex->second;
  29. mem.erase(pre);
  30. mem.erase(nex);
  31. mem.insert(make_pair(newl,newr));
  32. }
  33. }

查找空闲内存的时候暴力遍历整个set,复杂度O(能过)。


priority_queue维护的当前正在运行的进程,按照结束时间从小到大排序。

  1. struct task {
  2. ll st;//开始时间
  3. ll ed;//结束时间
  4. int space;//内存大小
  5. pair<int,int>p;//占用的内存区间
  6. task() {
  7. }
  8. task(ll _st,ll _ed,int _space,pair<int,int>_p) {
  9. st=_st;
  10. ed=_ed;
  11. space=_space;
  12. p=_p;
  13. }
  14. friend bool operator < (task p,task q) {
  15. return p.ed>q.ed;
  16. }
  17. };
  18. priority_queue<task>now_run;

等待队列直接用一个queue维护就好了。


每当一个进程请求内存的时候。先把正在运行的进程中,结束时间<请求时间的内存释放。等同一时刻结束的进程结束后,再处理等待队列。然后尝试把当前进程添加,如果空闲内存足够,则直接分配内存。否则加入等待队列。

代码

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<set>
  6. #include<map>
  7. #include<stack>
  8. #include<queue>
  9. #include<utility>
  10. #define maxm 10000
  11. #define INF 0x3f3f3f3f3f3f3f3f
  12. using namespace std;
  13. typedef long long ll;
  14. int n;
  15. int cnt;
  16. struct oper {
  17. ll t;
  18. int m;
  19. ll p;
  20. } a[maxm+5];
  21. typedef set< pair<int,int> >sp;
  22. typedef sp::iterator spi;
  23. sp mem;
  24. void new_mem(spi it,int sz){
  25. int l=it->first,r=it->second;
  26. mem.erase(it);
  27. if(l+sz<=r){
  28. mem.insert(make_pair(l+sz,r));
  29. }
  30. }
  31. void free_mem(pair<int,int>x) {
  32. if(mem.empty()){
  33. mem.insert(x);
  34. return;
  35. }
  36. spi pre=mem.lower_bound(x);
  37. if(pre!=mem.begin()) pre--;
  38. if(pre->second+1==x.first){
  39. int newl=pre->first;
  40. mem.erase(pre);
  41. pre=mem.insert(make_pair(newl,x.second)).first;
  42. //insert返回插入的位置
  43. //利用insert的返回值,找到合并后的区间,方便第二次合并
  44. }else pre=mem.insert(x).first;
  45. spi nex=mem.upper_bound(x);
  46. if(nex!=mem.end()&&x.second+1==nex->first){
  47. int newl=pre->first;
  48. int newr=nex->second;
  49. mem.erase(pre);
  50. mem.erase(nex);
  51. mem.insert(make_pair(newl,newr));
  52. }
  53. }
  54. pair<int,int> find_mem(int sz){
  55. for(spi it=mem.begin();it!=mem.end();it++){
  56. int l=it->first,r=it->second;
  57. if(r-l+1>=sz){
  58. new_mem(it,sz);
  59. return make_pair(l,l+sz-1);
  60. }
  61. }
  62. return make_pair(-1,-1);
  63. }
  64. void print_mem(){
  65. for(spi it=mem.begin();it!=mem.end();it++){
  66. int l=it->first,r=it->second;
  67. printf("[%d,%d] ",l,r);
  68. }
  69. printf("\n");
  70. }
  71. struct task {
  72. ll st;
  73. ll ed;
  74. int space;
  75. pair<int,int>p;
  76. task() {
  77. }
  78. task(ll _st,ll _ed,int _space,pair<int,int>_p) {
  79. st=_st;
  80. ed=_ed;
  81. space=_space;
  82. p=_p;
  83. }
  84. friend bool operator < (task p,task q) {
  85. return p.ed>q.ed;
  86. }
  87. };
  88. priority_queue<task>now_run;
  89. queue<int>now_wait;
  90. void pop_wait(ll tim) {
  91. while(!now_wait.empty()) {
  92. int id=now_wait.front();
  93. pair<int,int>tmp=find_mem(a[id].m);
  94. if(tmp.first!=-1) {
  95. now_run.push(task(tim,tim+a[id].p,a[id].m,tmp));
  96. now_wait.pop();
  97. } else break;
  98. }
  99. }
  100. ll last=0;
  101. void free_task(ll tim) {
  102. while(!now_run.empty()&&now_run.top().ed<=tim) {
  103. task tmp=now_run.top();
  104. free_mem(tmp.p);
  105. last=max(last,tmp.ed);
  106. now_run.pop();
  107. if(now_run.empty()||now_run.top().ed!=tmp.ed) pop_wait(tmp.ed);//等同一时刻结束的进程释放后,再处理等待的
  108. }
  109. }
  110. int inq_cnt=0;
  111. void add_task(int id) {
  112. free_task(a[id].t);
  113. pair<int,int>tmp=find_mem(a[id].m);
  114. if(tmp.first!=-1) {
  115. now_run.push(task(a[id].t,a[id].t+a[id].p,a[id].m,tmp));
  116. } else {
  117. inq_cnt++;
  118. now_wait.push(id);
  119. }
  120. }
  121. int main() {
  122. int u,v,w;
  123. scanf("%d",&n);
  124. while(scanf("%d %d %d",&u,&v,&w)!=EOF) {
  125. if(u==0&&v==0&&w==0) break;
  126. cnt++;
  127. a[cnt].t=u;
  128. a[cnt].m=v;
  129. a[cnt].p=w;
  130. }
  131. mem.insert(make_pair(0,n-1));
  132. for(int i=1; i<=cnt; i++) {
  133. add_task(i);
  134. }
  135. free_task(INF);
  136. printf("%lld\n%d\n",last,inq_cnt);
  137. }

[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. 微信小程序_(校园视)开发上传视频业务

    微信小程序_(校园视) 开发用户注册登陆 传送门 微信小程序_(校园视) 开发上传视频业务 传送门 微信小程序_(校园视) 开发视频的展示页-上 传送门 微信小程序_(校园视) 开发视频的展示页-下 ...

  2. JavaWeb-SpringBoot(抖音)_三、抖音项目后续

    JavaWeb-SpringBoot(抖音)_一.抖音项目制作 传送门 JavaWeb-SpringBoot(抖音)_二.服务器间通讯 传送门 JavaWeb-SpringBoot(抖音)_三.抖音项 ...

  3. eureka 服务实例实现快速下线快速感知快速刷新配置解析

    Spirng Eureka 默认配置解读 默认的Spring Eureka服务器,服务提供者和服务调用者配置不够灵敏,总是服务提供者在停掉很久之后,服务调用者很长时间并没有感知到变化.或者是服务已经注 ...

  4. webpack搭建多页面系统(一):对webpack 构建工具的理解

    为什么使用webpack构建工具? 1.开发效率方面: 在一般的开发过程中,分发好任务后,每个人完成自己单独的页面,如果有的人开发完成之后,接手别人的任务,就有可能造成开发时候的冲突. 如果利用模块化 ...

  5. git commit 合并到指定分支

    1. 将指定的commit合并到当前分支 git cherry-pick  commit_id 2. 合并多个连续 commit 到指定分支 假设需要合并 devlop 上从 fb407a3f 到 9 ...

  6. PHP 练习:租房子

    <form action="text.php" method="post"> 区域:<input type="checkbox&qu ...

  7. python 面对对象 类(继承, 多态)

    继承,继承其它实例化样本的属性和方法,需要在声明里重新定义和使用 class School(object): def __init__(self, name, addr): self.name = n ...

  8. 在oracle中使用基表建立月表的存储过程

    某些系统需要按月分表来保存数据.下面的存储过程演示了如何使用基表来建立每个月的月表. 处理思路是:     1:首先,为基表建立好表和对应的索引.     2:将基表保存到一个存储过程需要的表中.   ...

  9. vue文件流转换成pdf预览(pdf.js+iframe)

    参考文档:https://www.jianshu.com/p/242525315bf6 PDFJS: https://mozilla.github.io/pdf.js/     支持获取文件流到客户端 ...

  10. 【SQL】MySQL---using的用法

    学习记录: mysql中using的用法为: using()用于两张表的join查询,要求using()指定的列在两个表中均存在,并使用之用于join的条件