题目:[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;
}