Description

操作系统中一种重要的存储管理技术就是虚拟内存技术. 操作系统中允许进程同时运行, 也就是并行. 每个进程都有其相对独立的数据块 (进程运行的过程中将对其进行读写操作). 理想的情况下, 这些数据块都应该存放在内存中, 这样才能实现高效的读写操作. 但事实上, 内存的容量有限, 每个进程只能把一部分数据放在内存中, 为了解决这个矛盾, 提出了虚拟内存技术. 虚拟内存技术的基本原理是: 对进程而言, 内存空间是无限大的, 进程可以随意 地读写数据, 而对操作系统内部而言, 利用外存来模拟扩充的内存空间, 进程要求访问某个 内存单元时, 交由操作系统处理, 操作系统首先在内存中查找该单元是否存在, 如果存在, 查找成功, 否则转入外存查找 (一定存在于外存中). 就存储介质的物理性质而言, 内存的访问速度相对于外存要快得多, 因此对于每个进程来说操作系统应该把那些访问次数较多的 数据存放在内存中, 而把那些访问次数很少的数据放在外存中. 如何选择内存中暂留的数据 是一个很值得研究的问题, 下面介绍一个内存管理中比较常用的算法: 内存中的数据以页为基本存储单位, 进程的读写操作都针对页来进行. 实际内存空间被分割成 n 页, 虚拟内存空间 的页数往往要多得多. 某一时刻, 进程需要访问虚存编号为 P 的页, 该算法的执行步骤如下:

  1. 首先在内存中查找, 如果该页位于内存中, 查找成功, 转 4, 否则继续下面的操作;
  2. 寻找内存中是否存在空页 (即没有装载任何数据页的页面), 若有, 则从外存中读入要 查找页, 并将该页送至内存中的空页进行存储, 然后转 4, 否则继续下面的操作;
  3. 在内存中寻找一个访问次数最少的页面 (如果存在多个页面的访问次数同时为最少, 则选取最早读入数据进入内存的那个页面), 从外存中读入要查找页, 替换该页.
  4. 结束

所谓访问次数是指从当前页面进入内存到该时刻被访问的次数, 如果该页面以前进入过内存 并被其它页面替换, 那么前面的访问次数不应计入这个时刻的访问次数中. 你的任务是设计一个程序实现上述算法. 测试数据将会提供 \(m\) 条读写内存的命令, 每条命题提供要求访问 的虚拟内存页的编号 \(P\). 你的程序要求能够模拟整个 \(m\) 条命令的全部执行过程, 所有的命令是按照输入的先后执行的, 最开始的时候内存中的 \(n\) 页全为空.

Input

第 \(1\) 行为 \(n \lt 10000\) 和 \(m \lt 1000000\), 分别表示内存页数和读写内存命令条数. 接下来有 \(m\) 行, 其中第 \(i+1\) 行有一个正整数 \(P_i \leq 10^9\), 表示第 \(i\) 条读写内存命令需要访问的虚拟内存页的编号.

Output

仅包含一个正整数, 表示在整个模拟过程中, 在内存中直接查找成功的次数 (即上面的算法 只执行步骤 1 的次数).

Sample Input

3 8
1
1
2
3
4
2
5
4

Sample Output

1

Explanation

是不是随便拿 STL 水一下就可以过了呢~

其实用各种 set map 维护一发就可以了...... 就是逻辑可能有点混乱, 而且不停申请内存和销毁内存的开销可能有点大 (不过我的程序常数一直就很大对不对).

其实我的程序还是比较好看懂的, 就是各种 access_t 的定义太奇葩 233

话说这样说到底可以提高程序可读性, 降低检索次数 (就是查看这个变量是干嘛的)

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <map>
#include <set>

using namespace std;
typedef long long lli;
const int maxn = 10010;

typedef lli vmemaddr_t;
typedef int memaddr_t;
typedef int access_t;
typedef int loadtime_t;
const vmemaddr_t vmem_null = 0;
const memaddr_t mem_null = 0;

class VirtualMemoryManager
{
private:
    // Used to access memory address through virtual memory address
    map<vmemaddr_t, memaddr_t> vmem_to_mem;
    // Used to access virtual memory addresses through access counter
    map<access_t, map<loadtime_t, vmemaddr_t> > cnt_to_vmem;
    // Used to access virtual memory address stored in memory
    vmemaddr_t mem_to_vmem[maxn];
    // Used to store time variables for virtual memory addresses
    map<vmemaddr_t, access_t> vmem_to_cnt;
    map<vmemaddr_t, loadtime_t> vmem_to_tim;
    // Global stati
    set<memaddr_t> empty_mem;
    loadtime_t atomic_time;
    int n;
protected:
    bool cache_hit(vmemaddr_t vma)
    {
        if (vmem_to_mem[vma] > 0)
            return true;
        return false;
    }
    // void unload_memory(memaddr_t ma)
    // {
    //     vmemaddr_t vma = mem_to_vmem[ma];
    //     access_t act = vmem_to_cnt[vma];
    //     loadtime_t ltt = vmem_to_tim[vma];
    //     map<loadtime_t, vmemaddr_t>& lvm = cnt_to_vmem[act];
    //     lvm.erase(lvm.find(ltt));
    //     if (lvm.empty())
    //         cnt_to_vmem.erase(cnt_to_vmem.find(act));
    //     // Unloading indices
    //     mem_to_vmem[ma] = 0;
    //     vmem_to_mem.erase(vmem_to_mem.find(vma));
    //     vmem_to_cnt.erase(vmem_to_cnt.find(vma));
    //     vmem_to_tim.erase(vmem_to_tim.find(vma));
    //     empty_mem.insert(ma);
    //     return ;
    // }
public:
    bool access_memory(vmemaddr_t vma)
    {
        // printf("ACCESSING VMEM ADDRESS 0x%lld...\n", vma);
        // If cache hit, update counter and leave.
        if (this->cache_hit(vma)) {
            access_t act = vmem_to_cnt[vma];
            loadtime_t ltt = vmem_to_tim[vma];
            // printf("  CACHE HIT: ACCESSED %d, INJECTED @ %d\n", act, ltt);
            // Unloading original value.
            map<loadtime_t, vmemaddr_t>& lvm = cnt_to_vmem[act];
            lvm.erase(lvm.find(ltt));
            if (lvm.empty())
                cnt_to_vmem.erase(cnt_to_vmem.find(act));
            // Loading new value.
            act += 1;
            if (cnt_to_vmem.find(act) == cnt_to_vmem.end()) {
                map<loadtime_t, vmemaddr_t> lvm;
                cnt_to_vmem[act] = lvm;
            }
            cnt_to_vmem[act][ltt] = vma;
            // Finished loading. Updating indexes.
            vmem_to_cnt[vma] = act;
            return true;
        }
        // Insert into memory, if doesn't exist.
        // Freeing memory for extra spaces
        if (empty_mem.empty()) {
            // printf("  MEMORY FULL, LOOKING FOR EXCESSIVE SPACE\n", vma);
            map<access_t, map<loadtime_t, vmemaddr_t> >::iterator it1
                = cnt_to_vmem.begin();
            map<loadtime_t, vmemaddr_t>::iterator it2
                = it1->second.begin();
            vmemaddr_t vma = it2->second;
            // Eradicating from structure
            // printf("  DUMPING 0x%lld INTO PAGEFILE... ", vma);
            // this->unload_memory(vma);
            memaddr_t ma = vmem_to_mem[vma];
            access_t act = vmem_to_cnt[vma];
            loadtime_t ltt = vmem_to_tim[vma];
            it1->second.erase(it2);
            if (it1->second.empty())
                cnt_to_vmem.erase(it1);
            // printf("OK\n  REMOVING INDICES... ");
            mem_to_vmem[ma] = 0;
            vmem_to_mem.erase(vmem_to_mem.find(vma));
            vmem_to_cnt.erase(vmem_to_cnt.find(vma));
            vmem_to_tim.erase(vmem_to_tim.find(vma));
            empty_mem.insert(ma);
            // printf("OK\n");
        }
        // Injecting indices
        access_t act = 1;
        loadtime_t ltt = atomic_time;
        // printf("  INJECTING VMEM WITH SETTINGS %d, @ %d\n", act, ltt);
        vmem_to_cnt[vma] = act;
        vmem_to_tim[vma] = ltt;
        // Injecting into free memory
        memaddr_t ma = *empty_mem.begin();
        empty_mem.erase(empty_mem.begin());
        vmem_to_mem[vma] = ma;
        mem_to_vmem[ma] = vma;
        if (cnt_to_vmem.find(act) == cnt_to_vmem.end()) {
            map<loadtime_t, vmemaddr_t> lvm;
            cnt_to_vmem[act] = lvm;
        }
        cnt_to_vmem[act][ltt] = vma;
        // printf("  MEMORY CACHING SUCCEEDED\n");
        return false;
    }
    void increment_atomic_time(void)
    {
        atomic_time += 1;
        return ;
    }
    void init(int n_)
    {
        this->n = n_;
        atomic_time = 1;
        for (int i = 1; i <= n; i++) {
            vmem_to_mem[i] = 0;
            empty_mem.insert(i);
        }
        return ;
    }
} vmm;

int n, m;

int main(int argc, char** argv)
{
    scanf("%d%d", &n, &m);
    vmm.init(n);
    int res = 0;
    for (int i = 1; i <= m; i++) {
        vmemaddr_t vma;
        scanf("%lld", &vma);
        res += vmm.access_memory(vma);
        vmm.increment_atomic_time();
    }
    printf("%d\n", res);
    return 0;
}