hihocoder1198 Memory Allocating Algorithm(链表~)

题意:

小Hi和小Ho最近在研究内存分配的机制,他们写了一个比较简单的内存。内存可以表示成M个连续的存储空间,下标为0..M-1:

每当有数据写入时,内存分配程序会从下标0开始向右找一块足够存放下该数据的区域,将该数据写入。比如写入一个长度为2的数据,因为是第一个数据,我们用1来表示:

之后继续依次写入长度为3的数据和长度为2的数据,则有:

当数据足够多后,我们可能会遇到剩下的空间不足以写下新的数据。这时内存程序会从最早的数据开始进行删除。假设我们现在写到第8个数据把内存写满了:

这时我们需要写入第9个数据,该数据长度为4。则内存程序会删掉第1个数据:

但是仍然不够,于是删除掉第2个数据。这时就足够放下第9个数据了。于是内存程序存下第9个数据:

当有足够的空间存放数据数,数据总是尽可能靠左(起点下标尽可能小)存放。

小Hi和小Ho写好了内存之后,打算测试一下。他们会连续写入的N个数据,在所有数据写入完成后,他们想知道现在内存中各个存储单元的情况。

思路:

一个简单的思路是我们使用长度为M的数组来模拟整个内存使用的情况。

对于其中60%的数据:1≤N≤200,10≤M≤100,1≤K[i]≤5,这种简单的方法是可行的。

而对于另外40%的数据,由于M的长度最大可能为10^9,显然无法处理,因此我们需要进一步优化。

通过对简单数据的分析,我们可以发现这样一个情况:

一个相同的数字总是占用了一段连续的内存

那么我们可以使用(a,b)来表示一段长度为b的数字a,我们把这样表示的一段数据叫做一个数据块

比如内存情况1 1 1 1 1 2 2 3 3 0,则可以表示为:

(1,5),(2,2),(3,2),(0,1)

那么在简化的表示方法之下,我们应该如何来进行内存上的操作呢?

  • 添加一个数据(i, k[i])

在内存中寻找最左边的(0, b),同时满足b≥k[i]

b>k[i],将(0,b)分割为两个数据块(i, k[i]),(0, b-k[i])

b=k[i],将(0,b)直接改为(i, b)

记录第i个数据对应的(i, k[i])的位置,方便删除

  • 删除一个数据(i, k[i])

根据记录,找到到i的数据块(i, k[i]),同时找到其前一个块(prev, k[prev])和后一个块(next, k[next])

prev=0,讲(prev, k[prev])合并至(i, k[i])

next=0,讲(next, k[next])合并至(i, k[i])

(i,k[i])置为(0,k[i])

由于在简化的表示下,内存最多有2N个块,因此即便是顺序查找一个满足b≥k[i](0, b)数据块,需要的时间复杂度也不超过O(N)

而删除操作借助已经记录的数据位置,实现的时间复杂度为O(1)

根据题目,最多可能添加N个数据块,因此总的时间复杂度为O(N^2),可以解决问题。

有了算法,接下来就是考虑如何使用代码实现,这里我们采用链表来做:

对于每一个数据块,我们用如下方式表示:

Block {
    key: 表示数据编号,对应(a,b)中的a
    length: 表示数据长度,对应(a,b)中的b
    prev: 表示该数据块的前一个块
    next: 表示该数据块的后一个块
}

由于每一个块都有前后指针,因此为了简化处理,我们给链表加上头尾两个哨兵节点,得到初始的链表状态为:

(-1, 0), (0, M), (-1, 0)

初始化为:

init(M):
    head = new Block
    head -> key = -1
    head -> length = 0
    head -> prev = NULL
    head -> next = NULL

    p = new Block
    p -> key = 0
    p -> length = M
    p -> prev = head
    p -> next = NULL
    head -> next = p

    p2 = new Block
    p2 -> key = -1
    p2 -> length = 0
    p2 -> prev = p
    p2 -> next = NULL
    p -> next = p

这样就将初始的三个节点加入了链表,接下来就是几个操作。

  • 查找

直接按顺序进行扫描,查找符合要求的(0, b)

findEmpty(len):
    p = head -> next
    While (p -> key != -1)
        If (p -> key == 0 && p -> length >= len) Then
            Return p // 找到合适的空内存段
        End If
        p = p -> next
    End While
    Return NULL // 找不到空的内存
  • 添加

通过findEmpty找到的空数据块,并进行插入操作

insert(emptyBlock, key, len):
    // emptyBlock是找到的符合要求的空数据块
    If (emptyBlock -> lenght == len) Then
        emptyBlock -> key = key
    Else
        p = new Block // 新的(0, b - k[i])
        p -> key = 0
        p -> length = emptyBlock -> length - len

        p -> prev = emptyBlock
        p -> next = emptyBlock -> next
        p -> next -> prev = p

        emptyBlock -> key = key
        emptyBlock -> length = len
        emptyBlock -> next = p
    End If
    pos[ key ] = emptyBlock // 记录第key个数据的位置
  • 删除

通过pos[key]来获取数据块

delete(key):
    p = pos[ key ]

    tp = p -> prev // 合并前一个
    If (tp -> key == 0) Then
        p -> length = p -> length + tp -> length
        p -> prev = tp -> prev
        p -> prev -> next = p
        Delete tp
    End If

    tp = p -> next // 合并后一个
    If (tp -> key == 0) Then
        p -> length = p -> length + tp -> length
        p -> next = tp -> next
        p -> next -> prev = p
        Delete tp
    End If

    p -> key = 0
  • 主函数

如下

main()
    Input N, M
    init(M)
    lastDeleteData = 0
    For i = 1 .. N
        Input K
        While (true)
            p = findEmpty(K)
            If (p != NULL) Then
                insert(p, i, K)
                Break
            Else
                lastDeleteData = lastDeleteData + 1
                delete(lastDeleteData)
            End If
        End While
    End For
    //Type Ans 最后根据链表输出结果

本题在这次笔试中是作为中等偏简单的题目设计的,所以时间复杂度在O(N^2)的算法就能够拿满分。

而实际上本题还有O(NlogM)的线段树算法,这里留作大家思考。 如果你用O(NlogM)的线段树算法AC了这道题,欢迎跟帖分享。

/* ***********************************************
Author        :devil
************************************************ */
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <stdlib.h>
#define inf 0x3f3f3f3f
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dec(i,a,b) for(int i=a;i>=b;i--)
#define ou(a) printf("%d\n",a)
#define pb push_back
#define mkp make_pair
template<class T>inline void rd(T &x){char c=getchar();x=0;while(!isdigit(c))c=getchar();while(isdigit(c)){x=x*10+c-‘0‘;c=getchar();}}
#define IN freopen("in.txt","r",stdin);
#define OUT freopen("out.txt","w",stdout);
using namespace std;
const int mod=1e9+7;
const int N=2e3+10;
struct wq
{
    int id,len;
    wq *pre,*nxt;
};
int n,m,k,ans[N];
wq *head,*p,*p2,*tmp,*pos[N];
void init()
{
    head=new wq();
    head->id=-1;
    head->len=0;
    head->pre=NULL;
    head->nxt=NULL;
    p=new wq();
    p->id=0;
    p->len=m;
    p->pre=head;
    p->nxt=NULL;
    head->nxt=p;
    p2=new wq();
    p2->id=-1;
    p2->len=0;
    p2->pre=p;
    p2->nxt=NULL;
    p->nxt=p2;
}
wq *findEmpty(int l)
{
    p=head->nxt;
    while(p->id!=-1)
    {
        if(p->id==0&&p->len>=l) return p;
        p=p->nxt;
    }
    return NULL;
}
void Insert(wq *now,int id,int l)
{
    if(now->len==l) now->id=id;
    else
    {
        p=new wq();
        p->id=0;
        p->len=now->len-l;
        p->pre=now;
        p->nxt=now->nxt;
        p->nxt->pre=p;
        now->id=id;
        now->len=l;
        now->nxt=p;
    }
    pos[id]=now;
}
void Delete(int id)
{
    p=pos[id];
    tmp=p->pre;
    if(tmp->id==0)
    {
        p->len+=tmp->len;
        p->pre=tmp->pre;
        p->pre->nxt=p;
        delete tmp;
    }
    tmp=p->nxt;
    if(tmp->id==0)
    {
        p->len+=tmp->len;
        p->nxt=tmp->nxt;
        p->nxt->pre=p;
        delete tmp;
    }
    p->id=0;
}
int main()
{
    rd(n),rd(m);
    init();
    int last=0;
    rep(i,1,n)
    {
        rd(k);
        while(1)
        {
            p=findEmpty(k);
            if(p!=NULL)
            {
                Insert(p,i,k);
                break;
            }
            else
            {
                last++;
                Delete(last);
            }
        }
    }
    p=head->nxt;
    int start=0;
    memset(ans,-1,sizeof(ans));
    while(p->id!=-1)
    {
        if(p->id!=0) ans[p->id]=start;
        start+=p->len;
        p=p->nxt;
    }
    rep(i,0,n) if(ans[i]!=-1) printf("%d %d\n",i,ans[i]);
    return 0;
}

时间: 2024-08-02 10:57:01

hihocoder1198 Memory Allocating Algorithm(链表~)的相关文章

hihoCoder #1198 Memory Allocating Algorithm

Description Little Hi is studying on memory allocating algorithms this summer. He starts his experiments with a very simple algorithm. By this algorithm memory is considered as a sequence of M consecutive storage units, numbered from 0 to M-1. Whenev

PatentTips - Method to manage memory in a platform with virtual machines

BACKGROUND INFORMATION Various mechanisms exist for managing memory in a virtual machine environment. A virtual machine platform typically executes an underlying layer of software called a virtual machine monitor (VMM) which hosts one to many operati

C++ 内存池 -- C++ Memory Pool

这是我翻译的文章,来自 Code Project, 原文作者: DanDanger2000. 原文链接: http://www.codeproject.com/cpp/MemoryPool.asp C++ 内存池 l  下载示例工程 – 105Kb l  下载源代码 – 17.3Kb 目录l 引言l 它怎样工作l 示例l 使用这些代码l 好处l 关于代码l ToDol 历史 引言C/C++的内存分配(通过malloc或new)可能需要花费很多时.更糟糕的是,随着时间的流逝,内存(memory)将

160. Intersection of Two Linked Lists(找出两个链表的交点)

Write a program to find the node at which the intersection of two singly linked lists begins. For example, the following two linked lists: begin to intersect at node c1. Example 1: Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5],

Buddy system伙伴分配器实现

wikipedia:http://en.wikipedia.org/wiki/Buddy_memory_allocation The buddy memory allocation technique is a memory allocation algorithm that divides memory into partitions to try to satisfy a memory request as suitably as possible. This system makes us

内存管理模拟

内存管理模拟算法:首次适应算法.最佳适应算法.最坏适应算法 此程序是参考别人的,因此也没有什么好说的,感觉写的不错就贴上来了 代码如下: 1 #include<stdio.h> 2 #include<malloc.h> 3 #include<stdlib.h> 4 5 #define PROCESS_NAME_LEN 32 //进程名字长度 6 #define MIN_SLICE 10 //最小碎片大小 7 #define DEFAULT_MEM_SIZE 1024 /

Redis详解与常见问题解决方案

Redis简介 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sortedset --有序集合)和hash(哈希类型).这些数据类型都支持push/pop.add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的.在此基础上,redis支持各种不同方式的排序.与memcached一样,为了保证效率,数据都是缓存在内存中.区别的是redis会周期

内存分配---FF、BF、WF三种算法

动态分区分配是根据进程的实际需要,动态的为之分配内存空间.而在实现可变分区分配时,将涉及到分区分配中 所用的数据结构.分区分配算法和分区的分配与内存回收的过程. 分区分配中的数据结构:(1)描述空闲块的数据结构.(2)内存块的描述. #define PROCESS_NAME_LEN 32 //进程名长度 #define MIN_SLICE 10 //最小碎片的大小 #define DEFAULT_MEM_SIZE 1024 //内存大小 #define DEFAULT_MEM_START 0 /

不考虑性能的内存管理模拟系统(假内存)的C语言实现

#include<stdio.h>           /*头文件*/ #define PROCESS_NAME_LEN 32   /*进程名长度*/ #define MIN_SLICE    10             /*最小碎片的大小*/ #define DEFAULT_MEM_SIZE 1024     /*内存大小*/ #define DEFAULT_MEM_START 0       /*起始位置*/ #define FF 1             /*首次适应*/ #defi