[JSOI2010]缓存交换

题目链接
洛谷(测试示例更好)
牛客(示例有点问题,没有超过1e6以上的缓存编号)

题目:[JSOI2010]缓存交换

题目背景

感谢@ACdreamer 贡献数据

题目描述

在计算机中,CPU只能和高速缓存Cache直接交换数据。当所需的内存单元不在Cache中时,则需要从主存里把数据调入Cache。此时,如果Cache容量已满,则必须先从中删除一个。

例如,当前Cache容量为3,且已经有编号为10和20的主存单元。
此时,CPU访问编号为10的主存单元,Cache命中。

接着,CPU访问编号为21的主存单元,那么只需将该主存单元移入Cache中,造成一次缺失(Cache Miss)。

接着,CPU访问编号为31的主存单元,则必须从Cache中换出一块,才能将编号为31的主存单元移入Cache,假设我们移出了编号为10的主存单元。

接着,CPU再次访问编号为10的主存单元,则又引起了一次缺失。我们看到,如果在上一次删除时,删除其他的单元,则可以避免本次访问的缺失。

在现代计算机中,往往采用LRU(最近最少使用)的算法来进行Cache调度——可是,从上一个例子就能看出,这并不是最优的算法。
对于一个固定容量的空Cache和连续的若干主存访问请求,聪聪想知道如何在每次Cache缺失时换出正确的主存单元,以达到最少的Cache缺失次数。

输入格式

输入文件第一行包含两个整数N和M(1<=M<=N<=100,000),分别代表了主存访问的次数和Cache的容量。

第二行包含了N个空格分开的正整数,按访问请求先后顺序给出了每个主存块的编号(不超过1,000,000,000)。

输出格式

输出一行,为Cache缺失次数的最小值。

样例 #1

样例输入 #1

6 2
1 2 3 1 2 3

样例输出 #1

4

提示

在第4次缺失时将3号单元换出Cache。

代码

#include <bits/stdc++.h>
using namespace std;

/*
实现置换算法
OPT: Optimal,操作系统中最优置换算法,只在理想条件下才可用
warning
就算一个值已经出现在了 Cache 中,相应的信息也要更新!!!(不懂可以debug)
*/
void init() {
    
     ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); }
inline int read() {
    
    
    int x = 0, w = 1; char ch = getchar();
    for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
    return x * w;
}

struct node
{
    
    
    int index, value;
    friend bool operator<(node a, node b)
    {
    
         //从小到大排序采用“>”号;如果要从大到小排序,则采用“<”号
        return a.index < b.index;       //从大到小排序
    }
};
int n, m;
int num=0, ans=0;
const int SIZE = 1e5+10;
// 从当前位置i往后看,下一次i出现的位置
map<int, int> mp;
// 第i个数在不在缓存区内(下标),由于主存块的编号(不超过1,000,000,000)太大,因此采用map
map<int, int> in;
// 存第i个数下一次出现的位置下标
int nexti[SIZE];
int a[SIZE];
// 大根堆,能够将每次置换时把 缓冲区中下个使用时间 离当前最久 的置换出去
priority_queue<node> q;

int main()
{
    
    
    init();
    n = read(); 
    m = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    for (int i = n; i >= 1; --i)
    {
    
    
        // 未出现过,置为最大值
        if (mp[a[i]] == 0) nexti[i] = SIZE;
        else nexti[i] = mp[a[i]];// 下一次缓存块a[i]出现的位置
        mp[a[i]] = i;// 更新
    }
    mp.clear();// 没用了
    for (int i = 1; i <= n; ++i)
    {
    
    
        // 缺页错误
        if (in[a[i]] == 0)
        {
    
    
            ans++;
            // 缓冲区未满
            if (num < m)
            {
    
    
                num++;
            }
            else
            {
    
    
                // 把缓存区中元素中下一次出现最晚的那个给置换出去
                in[q.top().value] = 0;
                q.pop();
            }
            // 造成缺页的块放入了缓存
            in[a[i]] = 1;
        }
        node t;
        t.index = nexti[i];
        t.value = a[i];
        // 存 新放入的缓存块下一次访问的位置下标
        q.push(t);// 对于已经命中的缓存块而言,之前老旧的信息(上一个等于当前下标)无需删除,新信息的值一定会比老信息大
    }
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33843237/article/details/128050800