【BZOJ1826】【洛谷P4404】缓存交换【贪心】【堆】
题目大意:
题目链接:
BZOJ:https://www.lydsy.com/JudgeOnline/problem.php?id=1826
洛谷:https://www.luogu.org/problemnew/show/P4404
在计算机中,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缺失次数。
思路:
首先,当Cache满了之后,选择已有单元中下一次出现位置最后的删除是最优秀的。因为删除的位置越后,中间可以避免其他的缺失个数就越多,相对的缺失次数就会越少。
维护表示下一个和元素相同的元素位置。这个是可以求出来的。
然后我们要维护多个二元组,其中表示选择第个位置的元素,表示。我们要在选择的不超过个二元组中选择尽量大的删除。所以可以用优先队列来维护。
注意到若两个元素相同,设的位置在后面(入队时间比晚),那么必然有。所以其实可以不用删除前面的,因为必然可以覆盖掉,在的前面出队。
时间复杂度
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mp make_pair
using namespace std;
const int N=100010;
int n,m,tot,ans,sum,a[N],b[N],last[N],next[N];
priority_queue<pair<int,int> > q;
bool inque[N];
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);
tot=unique(b+1,b+1+n)-b-1;
for (int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
memset(last,0x3f3f3f3f,sizeof(last));
for (int i=n;i>=1;i--)
{
next[i]=last[a[i]];
last[a[i]]=i;
}
for (int i=1;i<=n;i++)
{
if (ans<m && !inque[a[i]])
{
ans++;
inque[a[i]]=1;
}
else if (ans>=m && !inque[a[i]])
{
ans++;
inque[a[i]]=1;
inque[q.top().second]=0;
q.pop();
}
q.push(mp(next[i],a[i]));
}
printf("%d\n",ans);
return 0;
}