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;
}